diff options
author | Chris Lattner <sabre@nondot.org> | 2010-06-08 16:52:24 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2010-06-08 16:52:24 +0000 |
commit | 30fdc8d841c9d24ac5f3d452b6ece84ee0ac991c (patch) | |
tree | f70013106f6a461a14abcd71c65f48a95a2979a6 /lldb/source/Target | |
parent | 312c4c799da215b337f790fda330f70c4aa757cf (diff) | |
download | bcm5719-llvm-30fdc8d841c9d24ac5f3d452b6ece84ee0ac991c.tar.gz bcm5719-llvm-30fdc8d841c9d24ac5f3d452b6ece84ee0ac991c.zip |
Initial checkin of lldb code from internal Apple repo.
llvm-svn: 105619
Diffstat (limited to 'lldb/source/Target')
28 files changed, 8659 insertions, 0 deletions
diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp new file mode 100644 index 00000000000..20d35e3dbb0 --- /dev/null +++ b/lldb/source/Target/ABI.cpp @@ -0,0 +1,47 @@ +//===-- ABI.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ABI.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +ABI* +ABI::FindPlugin (const ConstString &triple) +{ + std::auto_ptr<ABI> abi_ap; + ABICreateInstance create_callback; + + for (uint32_t idx = 0; + (create_callback = PluginManager::GetABICreateCallbackAtIndex(idx)) != NULL; + ++idx) + { + abi_ap.reset (create_callback(triple)); + + if (abi_ap.get()) + return abi_ap.release(); + } + + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +ABI::ABI() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ABI::~ABI() +{ +} diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp new file mode 100644 index 00000000000..e79f8c243ef --- /dev/null +++ b/lldb/source/Target/ExecutionContext.cpp @@ -0,0 +1,107 @@ +//===-- ExecutionContext.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// + +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +ExecutionContext::ExecutionContext() : + target (NULL), + process (NULL), + thread (NULL), + frame (NULL) +{ +} + +ExecutionContext::ExecutionContext (Target* t, bool fill_current_process_thread_frame) : + target (t), + process (NULL), + thread (NULL), + frame (NULL) +{ + if (t && fill_current_process_thread_frame) + { + process = t->GetProcessSP().get(); + if (process) + { + thread = process->GetThreadList().GetCurrentThread().get(); + if (thread) + frame = thread->GetCurrentFrame().get(); + } + } +} + +ExecutionContext::ExecutionContext(Process* p, Thread *t, StackFrame *f) : + target (p ? &p->GetTarget() : NULL), + process (p), + thread (t), + frame (f) +{ +} + +ExecutionContext::ExecutionContext (ExecutionContextScope *exe_scope_ptr) +{ + if (exe_scope_ptr) + exe_scope_ptr->Calculate (*this); + else + { + target = NULL; + process = NULL; + thread = NULL; + frame = NULL; + } +} + +ExecutionContext::ExecutionContext (ExecutionContextScope &exe_scope_ref) +{ + exe_scope_ref.Calculate (*this); +} + +void +ExecutionContext::Clear() +{ + target = NULL; + process = NULL; + thread = NULL; + frame = NULL; +} + + +RegisterContext * +ExecutionContext::GetRegisterContext () const +{ + if (frame) + return frame->GetRegisterContext(); + else if (thread) + return thread->GetRegisterContext(); + return NULL; +} + +ExecutionContextScope * +ExecutionContext::GetBestExecutionContextScope () const +{ + if (frame) + return frame; + if (thread) + return thread; + if (process) + return process; + return target; +} diff --git a/lldb/source/Target/ObjCObjectPrinter.cpp b/lldb/source/Target/ObjCObjectPrinter.cpp new file mode 100644 index 00000000000..81c73aa9581 --- /dev/null +++ b/lldb/source/Target/ObjCObjectPrinter.cpp @@ -0,0 +1,120 @@ +//===-- ObjCObjectPrinter.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "ObjCObjectPrinter.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ObjCObjectPrinter constructor +//---------------------------------------------------------------------- +ObjCObjectPrinter::ObjCObjectPrinter (Process &process) : + m_process(process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ObjCObjectPrinter::~ObjCObjectPrinter () +{ +} + +bool +ObjCObjectPrinter::PrintObject (ConstString &str, Value &object_ptr, ExecutionContext &exe_ctx) +{ + if (!exe_ctx.process) + return false; + + const Address *function_address = GetPrintForDebuggerAddr(); + + if (!function_address) + return false; + + const char *target_triple = exe_ctx.process->GetTargetTriple().GetCString(); + ClangASTContext *ast_context = exe_ctx.target->GetScratchClangASTContext(); + + void *return_qualtype = ast_context->GetCStringType(true); + Value ret; + ret.SetContext(Value::eContextTypeOpaqueClangQualType, return_qualtype); + + ValueList arg_value_list; + arg_value_list.PushValue(object_ptr); + + ClangFunction func(target_triple, ast_context, return_qualtype, *function_address, arg_value_list); + StreamString error_stream; + + lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; + func.InsertFunction(exe_ctx, wrapper_struct_addr, error_stream); + // FIXME: Check result of ExecuteFunction. + func.ExecuteFunction(exe_ctx, &wrapper_struct_addr, error_stream, true, 1000, true, ret); + + addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + + // poor man's strcpy + + size_t len = 0; + bool keep_reading = true; + Error error; + while (keep_reading) + { + char byte; + + if (exe_ctx.process->ReadMemory(result_ptr + len, &byte, 1, error) != 1) + return false; + + if (byte == '\0') + keep_reading = false; + else + ++len; + } + + char desc[len + 1]; + + if (exe_ctx.process->ReadMemory(result_ptr, &desc[0], len + 1, error) != len + 1) + return false; + + str.SetCString(desc); + + return true; +} + +Address * +ObjCObjectPrinter::GetPrintForDebuggerAddr() +{ + if (!m_PrintForDebugger_addr.get()) + { + ModuleList &modules = m_process.GetTarget().GetImages(); + + SymbolContextList contexts; + SymbolContext context; + + if((!modules.FindSymbolsWithNameAndType(ConstString ("_NSPrintForDebugger"), eSymbolTypeCode, contexts)) && + (!modules.FindSymbolsWithNameAndType(ConstString ("_CFPrintForDebugger"), eSymbolTypeCode, contexts))) + return NULL; + + contexts.GetContextAtIndex(0, context); + + m_PrintForDebugger_addr.reset(new Address(context.symbol->GetValue())); + } + + return m_PrintForDebugger_addr.get(); +} + diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp new file mode 100644 index 00000000000..02ca0671ca0 --- /dev/null +++ b/lldb/source/Target/PathMappingList.cpp @@ -0,0 +1,125 @@ +//===-- PathMappingList.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +// Project includes +#include "PathMappingList.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// PathMappingList constructor +//---------------------------------------------------------------------- +PathMappingList::PathMappingList +( + ChangedCallback callback, + void *callback_baton +) : + m_pairs (), + m_callback (callback), + m_callback_baton (callback_baton) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +PathMappingList::~PathMappingList () +{ +} + +void +PathMappingList::Append (const ConstString &path, + const ConstString &replacement, + bool notify) +{ + m_pairs.push_back(pair(path, replacement)); + if (notify && m_callback) + m_callback (*this, m_callback_baton); +} + +void +PathMappingList::Insert (const ConstString &path, + const ConstString &replacement, + uint32_t index, + bool notify) +{ + iterator insert_iter; + if (index >= m_pairs.size()) + insert_iter = m_pairs.end(); + else + insert_iter = m_pairs.begin() + index; + m_pairs.insert(insert_iter, pair(path, replacement)); + if (notify && m_callback) + m_callback (*this, m_callback_baton); +} + +bool +PathMappingList::Remove (off_t index, bool notify) +{ + if (index >= m_pairs.size()) + return false; + + iterator iter = m_pairs.begin() + index; + m_pairs.erase(iter); + if (notify && m_callback) + m_callback (*this, m_callback_baton); + return true; +} + +void +PathMappingList::Dump (Stream *s) +{ + unsigned int numPairs = m_pairs.size(); + unsigned int index; + + for (index = 0; index < numPairs; ++index) + { + s->Printf("[%d] \"%s\" -> \"%s\"\n", + index, m_pairs[index].first.GetCString(), m_pairs[index].second.GetCString()); + } +} + +void +PathMappingList::Clear (bool notify) +{ + m_pairs.clear(); + if (notify && m_callback) + m_callback (*this, m_callback_baton); +} + +size_t +PathMappingList::GetSize () +{ + return m_pairs.size(); +} + +bool +PathMappingList::RemapPath (const ConstString &path, ConstString &new_path) +{ + const_iterator pos, end = m_pairs.end(); + for (pos = m_pairs.begin(); pos != end; ++pos) + { + const size_t prefixLen = pos->first.GetLength(); + + if (::strncmp (pos->first.GetCString(), path.GetCString(), prefixLen) == 0) + { + std::string new_path_str (pos->second.GetCString()); + new_path_str.append(path.GetCString() + prefixLen); + new_path.SetCString(new_path_str.c_str()); + return true; + } + } + return false; +} diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp new file mode 100644 index 00000000000..9a6ab0c83e8 --- /dev/null +++ b/lldb/source/Target/Process.cpp @@ -0,0 +1,1876 @@ +//===-- Process.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Process.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +using namespace lldb; +using namespace lldb_private; + +Process* +Process::FindPlugin (Target &target, const char *plugin_name, Listener &listener) +{ + ProcessCreateInstance create_callback = NULL; + if (plugin_name) + { + create_callback = PluginManager::GetProcessCreateCallbackForPluginName (plugin_name); + if (create_callback) + { + std::auto_ptr<Process> debugger_ap(create_callback(target, listener)); + if (debugger_ap->CanDebug(target)) + return debugger_ap.release(); + } + } + else + { + for (uint32_t idx = 0; create_callback = PluginManager::GetProcessCreateCallbackAtIndex(idx); ++idx) + { + create_callback = PluginManager::GetProcessCreateCallbackAtIndex (idx); + if (create_callback) + { + std::auto_ptr<Process> debugger_ap(create_callback(target, listener)); + if (debugger_ap->CanDebug(target)) + return debugger_ap.release(); + } + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// Process constructor +//---------------------------------------------------------------------- +Process::Process(Target &target, Listener &listener) : + UserID (LLDB_INVALID_PROCESS_ID), + Broadcaster ("Process"), + m_target (target), + m_section_load_info (), + m_public_state (eStateUnloaded), + m_private_state (eStateUnloaded), + m_private_state_broadcaster ("lldb.process.internal_state_broadcaster"), + m_private_state_control_broadcaster ("lldb.process.internal_state_control_broadcaster"), + m_private_state_listener ("lldb.process.internal_state_listener"), + m_private_state_control_wait(), + m_private_state_thread (LLDB_INVALID_HOST_THREAD), + m_stop_id (0), + m_thread_index_id (0), + m_exit_status (-1), + m_exit_string (), + m_thread_list (this), + m_notifications (), + m_listener(listener), + m_unix_signals (), + m_objc_object_printer(*this) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Process::Process()", this); + + listener.StartListeningForEvents (this, + eBroadcastBitStateChanged | + eBroadcastBitInterrupt | + eBroadcastBitSTDOUT | + eBroadcastBitSTDERR); + + m_private_state_listener.StartListeningForEvents(&m_private_state_broadcaster, + eBroadcastBitStateChanged); + + m_private_state_listener.StartListeningForEvents(&m_private_state_control_broadcaster, + eBroadcastInternalStateControlStop | + eBroadcastInternalStateControlPause | + eBroadcastInternalStateControlResume); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Process::~Process() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Process::~Process()", this); + StopPrivateStateThread(); +} + +void +Process::Finalize() +{ + // Do any cleanup needed prior to being destructed... Subclasses + // that override this method should call this superclass method as well. +} + +void +Process::RegisterNotificationCallbacks (const Notifications& callbacks) +{ + m_notifications.push_back(callbacks); + if (callbacks.initialize != NULL) + callbacks.initialize (callbacks.baton, this); +} + +bool +Process::UnregisterNotificationCallbacks(const Notifications& callbacks) +{ + std::vector<Notifications>::iterator pos, end = m_notifications.end(); + for (pos = m_notifications.begin(); pos != end; ++pos) + { + if (pos->baton == callbacks.baton && + pos->initialize == callbacks.initialize && + pos->process_state_changed == callbacks.process_state_changed) + { + m_notifications.erase(pos); + return true; + } + } + return false; +} + +void +Process::SynchronouslyNotifyStateChanged (StateType state) +{ + std::vector<Notifications>::iterator notification_pos, notification_end = m_notifications.end(); + for (notification_pos = m_notifications.begin(); notification_pos != notification_end; ++notification_pos) + { + if (notification_pos->process_state_changed) + notification_pos->process_state_changed (notification_pos->baton, this, state); + } +} + +// FIXME: We need to do some work on events before the general Listener sees them. +// For instance if we are continuing from a breakpoint, we need to ensure that we do +// the little "insert real insn, step & stop" trick. But we can't do that when the +// event is delivered by the broadcaster - since that is done on the thread that is +// waiting for new events, so if we needed more than one event for our handling, we would +// stall. So instead we do it when we fetch the event off of the queue. +// + +StateType +Process::GetNextEvent (EventSP &event_sp) +{ + StateType state = eStateInvalid; + + if (m_listener.GetNextEventForBroadcaster (this, event_sp) && event_sp) + state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + + return state; +} + + +StateType +Process::WaitForProcessToStop (const TimeValue *timeout) +{ + StateType match_states[] = { eStateStopped, eStateCrashed, eStateDetached, eStateExited, eStateUnloaded }; + return WaitForState (timeout, match_states, sizeof(match_states) / sizeof(StateType)); +} + + +StateType +Process::WaitForState +( + const TimeValue *timeout, + const StateType *match_states, const uint32_t num_match_states +) +{ + EventSP event_sp; + uint32_t i; + StateType state = eStateUnloaded; + while (state != eStateInvalid) + { + state = WaitForStateChangedEvents (timeout, event_sp); + + for (i=0; i<num_match_states; ++i) + { + if (match_states[i] == state) + return state; + } + } + return state; +} + +StateType +Process::WaitForStateChangedEvents (const TimeValue *timeout, EventSP &event_sp) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout); + + StateType state = eStateInvalid; + if (m_listener.WaitForEventForBroadcasterWithType(timeout, + this, + eBroadcastBitStateChanged, + event_sp)) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp) => %s", + __FUNCTION__, + timeout, + StateAsCString(state)); + return state; +} + +Event * +Process::PeekAtStateChangedEvents () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s...", __FUNCTION__); + + Event *event_ptr; + event_ptr = m_listener.PeekAtNextEventForBroadcasterWithType(this, + eBroadcastBitStateChanged); + if (log) + { + if (event_ptr) + { + log->Printf ("Process::%s (event_ptr) => %s", + __FUNCTION__, + StateAsCString(ProcessEventData::GetStateFromEvent (event_ptr))); + } + else + { + log->Printf ("Process::%s no events found", + __FUNCTION__); + } + } + return event_ptr; +} + +StateType +Process::WaitForStateChangedEventsPrivate (const TimeValue *timeout, EventSP &event_sp) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout); + + StateType state = eStateInvalid; + if (m_private_state_listener.WaitForEventForBroadcasterWithType(timeout, + &m_private_state_broadcaster, + eBroadcastBitStateChanged, + event_sp)) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + // This is a bit of a hack, but when we wait here we could very well return + // to the command-line, and that could disable the log, which would render the + // log we got above invalid. + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp) => %s", __FUNCTION__, timeout, StateAsCString(state)); + return state; +} + +bool +Process::WaitForEventsPrivate (const TimeValue *timeout, EventSP &event_sp, bool control_only) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout); + + if (control_only) + return m_private_state_listener.WaitForEventForBroadcaster(timeout, &m_private_state_control_broadcaster, event_sp); + else + return m_private_state_listener.WaitForEvent(timeout, event_sp); +} + +bool +Process::IsRunning () const +{ + return StateIsRunningState (m_public_state.GetValue()); +} + +int +Process::GetExitStatus () +{ + if (m_public_state.GetValue() == eStateExited) + return m_exit_status; + return -1; +} + +const char * +Process::GetExitDescription () +{ + if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) + return m_exit_string.c_str(); + return NULL; +} + +void +Process::SetExitStatus (int status, const char *cstr) +{ + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + + SetPrivateState (eStateExited); +} + +// This static callback can be used to watch for local child processes on +// the current host. The the child process exits, the process will be +// found in the global target list (we want to be completely sure that the +// lldb_private::Process doesn't go away before we can deliver the signal. +bool +Process::SetProcessExitStatus +( + void *callback_baton, + lldb::pid_t pid, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero +) +{ + if (signo == 0 || exit_status) + { + TargetSP target_sp(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcessID (pid)); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + { + const char *signal_cstr = NULL; + if (signo) + signal_cstr = process_sp->GetUnixSignals().GetSignalAsCString (signo); + + process_sp->SetExitStatus (exit_status, signal_cstr); + } + } + return true; + } + return false; +} + + +uint32_t +Process::GetNextThreadIndexID () +{ + return ++m_thread_index_id; +} + +StateType +Process::GetState() +{ + // If any other threads access this we will need a mutex for it + return m_public_state.GetValue (); +} + +void +Process::SetPublicState (StateType new_state) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE); + if (log) + log->Printf("Process::SetPublicState (%s)", StateAsCString(new_state)); + m_public_state.SetValue (new_state); +} + +StateType +Process::GetPrivateState () +{ + return m_private_state.GetValue(); +} + +void +Process::SetPrivateState (StateType new_state) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE); + bool state_changed = false; + + if (log) + log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state)); + + Mutex::Locker locker(m_private_state.GetMutex()); + + const StateType old_state = m_private_state.GetValueNoLock (); + state_changed = old_state != new_state; + if (state_changed) + { + m_private_state.SetValueNoLock (new_state); + if (StateIsStoppedState(new_state)) + { + m_stop_id++; + if (log) + log->Printf("Process::SetPrivateState (%s) stop_id = %u", StateAsCString(new_state), m_stop_id); + } + // Use our target to get a shared pointer to ourselves... + m_private_state_broadcaster.BroadcastEvent (eBroadcastBitStateChanged, new ProcessEventData (GetTarget().GetProcessSP(), new_state)); + } + else + { + if (log) + log->Printf("Process::SetPrivateState (%s) state didn't change. Ignoring...", StateAsCString(new_state), StateAsCString(old_state)); + } +} + + +uint32_t +Process::GetStopID() const +{ + return m_stop_id; +} + +addr_t +Process::GetImageInfoAddress() +{ + return LLDB_INVALID_ADDRESS; +} + +DynamicLoader * +Process::GetDynamicLoader() +{ + return NULL; +} + +const ABI * +Process::GetABI() +{ + ConstString& triple = m_target_triple; + + if (triple.IsEmpty()) + return NULL; + + if (m_abi_sp.get() == NULL) + { + m_abi_sp.reset(ABI::FindPlugin(triple)); + } + + return m_abi_sp.get(); +} + +BreakpointSiteList & +Process::GetBreakpointSiteList() +{ + return m_breakpoint_site_list; +} + +const BreakpointSiteList & +Process::GetBreakpointSiteList() const +{ + return m_breakpoint_site_list; +} + + +void +Process::DisableAllBreakpointSites () +{ + m_breakpoint_site_list.SetEnabledForAll (false); +} + +Error +Process::ClearBreakpointSiteByID (lldb::user_id_t break_id) +{ + Error error (DisableBreakpointSiteByID (break_id)); + + if (error.Success()) + m_breakpoint_site_list.Remove(break_id); + + return error; +} + +Error +Process::DisableBreakpointSiteByID (lldb::user_id_t break_id) +{ + Error error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id); + if (bp_site_sp) + { + if (bp_site_sp->IsEnabled()) + error = DisableBreakpoint (bp_site_sp.get()); + } + else + { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %i", break_id); + } + + return error; +} + +Error +Process::EnableBreakpointSiteByID (lldb::user_id_t break_id) +{ + Error error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id); + if (bp_site_sp) + { + if (!bp_site_sp->IsEnabled()) + error = EnableBreakpoint (bp_site_sp.get()); + } + else + { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %i", break_id); + } + return error; +} + +lldb::user_id_t +Process::CreateBreakpointSite (BreakpointLocationSP &owner, bool use_hardware) +{ + const addr_t load_addr = owner->GetAddress().GetLoadAddress (this); + if (load_addr != LLDB_INVALID_ADDRESS) + { + BreakpointSiteSP bp_site_sp; + + // Look up this breakpoint site. If it exists, then add this new owner, otherwise + // create a new breakpoint site and add it. + + bp_site_sp = m_breakpoint_site_list.FindByAddress (load_addr); + + if (bp_site_sp) + { + bp_site_sp->AddOwner (owner); + owner->SetBreakpointSite (bp_site_sp); + return bp_site_sp->GetID(); + } + else + { + bp_site_sp.reset (new BreakpointSite (&m_breakpoint_site_list, owner, load_addr, LLDB_INVALID_THREAD_ID, use_hardware)); + if (bp_site_sp) + { + if (EnableBreakpoint (bp_site_sp.get()).Success()) + { + owner->SetBreakpointSite (bp_site_sp); + return m_breakpoint_site_list.Add (bp_site_sp); + } + } + } + } + // We failed to enable the breakpoint + return LLDB_INVALID_BREAK_ID; + +} + +void +Process::RemoveOwnerFromBreakpointSite (lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, BreakpointSiteSP &bp_site_sp) +{ + uint32_t num_owners = bp_site_sp->RemoveOwner (owner_id, owner_loc_id); + if (num_owners == 0) + { + DisableBreakpoint(bp_site_sp.get()); + m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress()); + } +} + + +size_t +Process::RemoveBreakpointOpcodesFromBuffer (addr_t bp_addr, size_t size, uint8_t *buf) const +{ + size_t bytes_removed = 0; + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + size_t idx; + BreakpointSiteSP bp; + + for (idx = 0; (bp = m_breakpoint_site_list.GetByIndex(idx)) != NULL; ++idx) + { + if (bp->GetType() == BreakpointSite::eSoftware) + { + if (bp->IntersectsRange(bp_addr, size, &intersect_addr, &intersect_size, &opcode_offset)) + { + assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size); + assert(bp_addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= bp_addr + size); + assert(opcode_offset + intersect_size <= bp->GetByteSize()); + size_t buf_offset = intersect_addr - bp_addr; + ::memcpy(buf + buf_offset, bp->GetSavedOpcodeBytes() + opcode_offset, intersect_size); + } + } + } + return bytes_removed; +} + + +Error +Process::EnableSoftwareBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + const addr_t bp_addr = bp_site->GetLoadAddress(); + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx", bp_site->GetID(), (uint64_t)bp_addr); + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- already enabled", bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + if (bp_addr == LLDB_INVALID_ADDRESS) + { + error.SetErrorString("BreakpointSite contains an invalid load address."); + return error; + } + // Ask the lldb::Process subclass to fill in the correct software breakpoint + // trap for the breakpoint site + const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site); + + if (bp_opcode_size == 0) + { + error.SetErrorStringWithFormat ("Process::GetSoftwareBreakpointTrapOpcode() returned zero, unable to get breakpoint trap for address 0x%llx.\n", bp_addr); + } + else + { + const uint8_t * const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes(); + + if (bp_opcode_bytes == NULL) + { + error.SetErrorString ("BreakpointSite doesn't contain a valid breakpoint trap opcode."); + return error; + } + + // Save the original opcode by reading it + if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, error) == bp_opcode_size) + { + // Write a software breakpoint in place of the original opcode + if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) + { + uint8_t verify_bp_opcode_bytes[64]; + if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size) + { + if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, bp_opcode_size) == 0) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eSoftware); + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- SUCCESS", + bp_site->GetID(), + (uint64_t)bp_addr); + } + else + error.SetErrorString("Failed to verify the breakpoint trap in memory."); + } + else + error.SetErrorString("Unable to read memory to verify breakpoint trap."); + } + else + error.SetErrorString("Unable to write breakpoint trap to memory."); + } + else + error.SetErrorString("Unable to read memory at breakpoint address."); + } + if (log) + log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- FAILED: %s", + bp_site->GetID(), + (uint64_t)bp_addr, + error.AsCString()); + return error; +} + +Error +Process::DisableSoftwareBreakpoint (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + addr_t bp_addr = bp_site->GetLoadAddress(); + lldb::user_id_t breakID = bp_site->GetID(); + if (log) + log->Printf ("ProcessMacOSX::DisableBreakpoint (breakID = %d) addr = 0x%llx", breakID, (uint64_t)bp_addr); + + if (bp_site->IsHardware()) + { + error.SetErrorString("Breakpoint site is a hardware breakpoint."); + } + else if (bp_site->IsEnabled()) + { + const size_t break_op_size = bp_site->GetByteSize(); + const uint8_t * const break_op = bp_site->GetTrapOpcodeBytes(); + if (break_op_size > 0) + { + // Clear a software breakoint instruction + uint8_t curr_break_op[break_op_size]; + bool break_op_found = false; + + // Read the breakpoint opcode + if (DoReadMemory (bp_addr, curr_break_op, break_op_size, error) == break_op_size) + { + bool verify = false; + // Make sure we have the a breakpoint opcode exists at this address + if (::memcmp (curr_break_op, break_op, break_op_size) == 0) + { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (DoWriteMemory (bp_addr, bp_site->GetSavedOpcodeBytes(), break_op_size, error) == break_op_size) + { + verify = true; + } + else + error.SetErrorString("Memory write failed when restoring original opcode."); + } + else + { + error.SetErrorString("Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has already been restored + verify = true; + } + + if (verify) + { + uint8_t verify_opcode[break_op_size]; + // Verify that our original opcode made it back to the inferior + if (DoReadMemory (bp_addr, verify_opcode, break_op_size, error) == break_op_size) + { + // compare the memory we just read with the original opcode + if (::memcmp (bp_site->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0) + { + // SUCCESS + bp_site->SetEnabled(false); + if (log) + log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + else + { + if (break_op_found) + error.SetErrorString("Failed to restore original opcode."); + } + } + else + error.SetErrorString("Failed to read memory to verify that breakpoint trap was restored."); + } + } + else + error.SetErrorString("Unable to read memory that should contain the breakpoint trap."); + } + } + else + { + if (log) + log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- already disabled", bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + if (log) + log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%llx -- FAILED: %s", + bp_site->GetID(), + (uint64_t)bp_addr, + error.AsCString()); + return error; + +} + + +size_t +Process::ReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + if (buf == NULL || size == 0) + return 0; + + size_t bytes_read = 0; + uint8_t *bytes = (uint8_t *)buf; + + while (bytes_read < size) + { + const size_t curr_size = size - bytes_read; + const size_t curr_bytes_read = DoReadMemory (addr + bytes_read, + bytes + bytes_read, + curr_size, + error); + bytes_read += curr_bytes_read; + if (curr_bytes_read == curr_size || curr_bytes_read == 0) + break; + } + + // Replace any software breakpoint opcodes that fall into this range back + // into "buf" before we return + if (bytes_read > 0) + RemoveBreakpointOpcodesFromBuffer (addr, bytes_read, (uint8_t *)buf); + return bytes_read; +} + +size_t +Process::WriteMemoryPrivate (addr_t addr, const void *buf, size_t size, Error &error) +{ + size_t bytes_written = 0; + const uint8_t *bytes = (const uint8_t *)buf; + + while (bytes_written < size) + { + const size_t curr_size = size - bytes_written; + const size_t curr_bytes_written = DoWriteMemory (addr + bytes_written, + bytes + bytes_written, + curr_size, + error); + bytes_written += curr_bytes_written; + if (curr_bytes_written == curr_size || curr_bytes_written == 0) + break; + } + return bytes_written; +} + +size_t +Process::WriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + if (buf == NULL || size == 0) + return 0; + // We need to write any data that would go where any current software traps + // (enabled software breakpoints) any software traps (breakpoints) that we + // may have placed in our tasks memory. + + BreakpointSiteList::collection::const_iterator iter = m_breakpoint_site_list.GetMap()->lower_bound (addr); + BreakpointSiteList::collection::const_iterator end = m_breakpoint_site_list.GetMap()->end(); + + if (iter == end || iter->second->GetLoadAddress() > addr + size) + return DoWriteMemory(addr, buf, size, error); + + BreakpointSiteList::collection::const_iterator pos; + size_t bytes_written = 0; + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + const uint8_t *ubuf = (const uint8_t *)buf; + + for (pos = iter; pos != end; ++pos) + { + BreakpointSiteSP bp; + bp = pos->second; + + assert(bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset)); + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->GetByteSize()); + + // Check for bytes before this breakpoint + const addr_t curr_addr = addr + bytes_written; + if (intersect_addr > curr_addr) + { + // There are some bytes before this breakpoint that we need to + // just write to memory + size_t curr_size = intersect_addr - curr_addr; + size_t curr_bytes_written = WriteMemoryPrivate (curr_addr, + ubuf + bytes_written, + curr_size, + error); + bytes_written += curr_bytes_written; + if (curr_bytes_written != curr_size) + { + // We weren't able to write all of the requested bytes, we + // are done looping and will return the number of bytes that + // we have written so far. + break; + } + } + + // Now write any bytes that would cover up any software breakpoints + // directly into the breakpoint opcode buffer + ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size); + bytes_written += intersect_size; + } + + // Write any remaining bytes after the last breakpoint if we have any left + if (bytes_written < size) + bytes_written += WriteMemoryPrivate (addr + bytes_written, + ubuf + bytes_written, + size - bytes_written, + error); + + return bytes_written; +} + +addr_t +Process::AllocateMemory(size_t size, uint32_t permissions, Error &error) +{ + // Fixme: we should track the blocks we've allocated, and clean them up... + // We could even do our own allocator here if that ends up being more efficient. + return DoAllocateMemory (size, permissions, error); +} + +Error +Process::DeallocateMemory (addr_t ptr) +{ + return DoDeallocateMemory (ptr); +} + + +Error +Process::EnableWatchpoint (WatchpointLocation *watchpoint) +{ + Error error; + error.SetErrorString("watchpoints are not supported"); + return error; +} + +Error +Process::DisableWatchpoint (WatchpointLocation *watchpoint) +{ + Error error; + error.SetErrorString("watchpoints are not supported"); + return error; +} + +StateType +Process::WaitForProcessStopPrivate (const TimeValue *timeout, EventSP &event_sp) +{ + StateType state; + // Now wait for the process to launch and return control to us, and then + // call DidLaunch: + while (1) + { + // FIXME: Might want to put a timeout in here: + state = WaitForStateChangedEventsPrivate (NULL, event_sp); + if (state == eStateStopped || state == eStateCrashed || state == eStateExited) + break; + else + HandlePrivateEvent (event_sp); + } + return state; +} + +Error +Process::Launch +( + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path +) +{ + Error error; + m_target_triple.Clear(); + m_abi_sp.reset(); + + Module *exe_module = m_target.GetExecutableModule().get(); + if (exe_module) + { + char exec_file_path[PATH_MAX]; + exe_module->GetFileSpec().GetPath(exec_file_path, sizeof(exec_file_path)); + if (exe_module->GetFileSpec().Exists()) + { + error = WillLaunch (exe_module); + if (error.Success()) + { + // The args coming in should not contain the application name, the + // lldb_private::Process class will add this in case the executable + // gets resolved to a different file than was given on the command + // line (like when an applicaiton bundle is specified and will + // resolve to the contained exectuable file, or the file given was + // a symlink or other file system link that resolves to a different + // file). + + // Get the resolved exectuable path + + // Make a new argument vector + std::vector<const char *> exec_path_plus_argv; + // Append the resolved executable path + exec_path_plus_argv.push_back (exec_file_path); + + // Push all args if there are any + if (argv) + { + for (int i = 0; argv[i]; ++i) + exec_path_plus_argv.push_back(argv[i]); + } + + // Push a NULL to terminate the args. + exec_path_plus_argv.push_back(NULL); + + // Now launch using these arguments. + error = DoLaunch (exe_module, exec_path_plus_argv.data(), envp, stdin_path, stdout_path, stderr_path); + + if (error.Fail()) + { + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + SetID (LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "launch failed"; + SetExitStatus (-1, error_string); + } + } + else + { + EventSP event_sp; + StateType state = WaitForProcessStopPrivate(NULL, event_sp); + + if (state == eStateStopped || state == eStateCrashed) + { + DidLaunch (); + + // This delays passing the stopped event to listeners till DidLaunch gets + // a chance to complete... + HandlePrivateEvent (event_sp); + StartPrivateStateThread (); + } + else if (state == eStateExited) + { + // We exited while trying to launch somehow. Don't call DidLaunch as that's + // not likely to work, and return an invalid pid. + HandlePrivateEvent (event_sp); + } + } + } + } + else + { + error.SetErrorStringWithFormat("File doesn't exist: '%s'.\n", exec_file_path); + } + } + return error; +} + +Error +Process::CompleteAttach () +{ + Error error; + EventSP event_sp; + StateType state = WaitForProcessStopPrivate(NULL, event_sp); + if (state == eStateStopped || state == eStateCrashed) + { + DidAttach (); + + // This delays passing the stopped event to listeners till DidLaunch gets + // a chance to complete... + HandlePrivateEvent(event_sp); + StartPrivateStateThread(); + } + else + { + // We exited while trying to launch somehow. Don't call DidLaunch as that's + // not likely to work, and return an invalid pid. + if (state == eStateExited) + HandlePrivateEvent (event_sp); + error.SetErrorStringWithFormat("invalid state after attach: %s", + lldb_private::StateAsCString(state)); + } + return error; +} + +Error +Process::Attach (lldb::pid_t attach_pid) +{ + + m_target_triple.Clear(); + m_abi_sp.reset(); + + Error error(WillAttach (attach_pid)); + if (error.Success()) + { + error = DoAttach (attach_pid); + if (error.Success()) + { + error = CompleteAttach(); + } + else + { + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + SetID (LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); + } + } + } + return error; +} + +Error +Process::Attach (const char *process_name, bool wait_for_launch) +{ + m_target_triple.Clear(); + m_abi_sp.reset(); + + Error error (WillAttach (process_name, wait_for_launch)); + if (error.Success()) + { + StartPrivateStateThread(); + error = DoAttach (process_name, wait_for_launch); + if (error.Fail()) + { + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + SetID (LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); + } + } + else + { + error = CompleteAttach(); + } + } + return error; +} + +Error +Process::Resume () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf("Process::Resume() m_stop_id = %u", m_stop_id); + + Error error (WillResume()); + // Tell the process it is about to resume before the thread list + if (error.Success()) + { + // Now let the thread list know we are about to resume to it + // can let all of our threads know that they are about to be + // resumed. Threads will each be called with + // Thread::WillResume(StateType) where StateType contains the state + // that they are supposed to have when the process is resumed + // (suspended/running/stepping). Threads should also check + // their resume signal in lldb::Thread::GetResumeSignal() + // to see if they are suppoed to start back up with a signal. + if (m_thread_list.WillResume()) + { + error = DoResume(); + if (error.Success()) + { + DidResume(); + m_thread_list.DidResume(); + } + } + else + { + error.SetErrorStringWithFormat("thread list returned flase after WillResume"); + } + } + return error; +} + +Error +Process::Halt () +{ + Error error (WillHalt()); + + if (error.Success()) + { + error = DoHalt(); + if (error.Success()) + DidHalt(); + } + return error; +} + +Error +Process::Detach () +{ + Error error (WillDetach()); + + if (error.Success()) + { + DisableAllBreakpointSites(); + error = DoDetach(); + if (error.Success()) + { + DidDetach(); + StopPrivateStateThread(); + } + } + return error; +} + +Error +Process::Destroy () +{ + Error error (WillDestroy()); + if (error.Success()) + { + DisableAllBreakpointSites(); + error = DoDestroy(); + if (error.Success()) + { + DidDestroy(); + StopPrivateStateThread(); + } + } + return error; +} + +Error +Process::Signal (int signal) +{ + Error error (WillSignal()); + if (error.Success()) + { + error = DoSignal(signal); + if (error.Success()) + DidSignal(); + } + return error; +} + +UnixSignals & +Process::GetUnixSignals () +{ + return m_unix_signals; +} + +Target & +Process::GetTarget () +{ + return m_target; +} + +const Target & +Process::GetTarget () const +{ + return m_target; +} + +uint32_t +Process::GetAddressByteSize() +{ + return m_target.GetArchitecture().GetAddressByteSize(); +} + +bool +Process::ShouldBroadcastEvent (Event *event_ptr) +{ + const StateType state = Process::ProcessEventData::GetStateFromEvent (event_ptr); + bool return_value = true; + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); + + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateDetached: + case eStateExited: + case eStateUnloaded: + // These events indicate changes in the state of the debugging session, always report them. + return_value = true; + break; + case eStateInvalid: + // We stopped for no apparent reason, don't report it. + return_value = false; + break; + case eStateRunning: + case eStateStepping: + // If we've started the target running, we handle the cases where we + // are already running and where there is a transition from stopped to + // running differently. + // running -> running: Automatically suppress extra running events + // stopped -> running: Report except when there is one or more no votes + // and no yes votes. + SynchronouslyNotifyStateChanged (state); + switch (m_public_state.GetValue()) + { + case eStateRunning: + case eStateStepping: + // We always suppress multiple runnings with no PUBLIC stop in between. + return_value = false; + break; + default: + // TODO: make this work correctly. For now always report + // run if we aren't running so we don't miss any runnning + // events. If I run the lldb/test/thread/a.out file and + // break at main.cpp:58, run and hit the breakpoints on + // multiple threads, then somehow during the stepping over + // of all breakpoints no run gets reported. + return_value = true; + + // This is a transition from stop to run. + switch (m_thread_list.ShouldReportRun (event_ptr)) + { + case eVoteYes: + case eVoteNoOpinion: + return_value = true; + break; + case eVoteNo: + return_value = false; + break; + } + break; + } + break; + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + { + // We've stopped. First see if we're going to restart the target. + // If we are going to stop, then we always broadcast the event. + // If we aren't going to stop, let the thread plans decide if we're going to report this event. + // If no thread has an opinion, we also report it. + if (state != eStateInvalid) + { + + RefreshStateAfterStop (); + + if (m_thread_list.ShouldStop (event_ptr) == false) + { + switch (m_thread_list.ShouldReportStop (event_ptr)) + { + case eVoteYes: + Process::ProcessEventData::SetRestartedInEvent (event_ptr, true); + case eVoteNoOpinion: + return_value = true; + break; + case eVoteNo: + return_value = false; + break; + } + + if (log) + log->Printf ("Process::ShouldBroadcastEvent (%p) Restarting process", event_ptr, StateAsCString(state)); + Resume (); + } + else + { + return_value = true; + SynchronouslyNotifyStateChanged (state); + } + } + } + } + + if (log) + log->Printf ("Process::ShouldBroadcastEvent (%p) => %s", event_ptr, StateAsCString(state), return_value ? "YES" : "NO"); + return return_value; +} + +//------------------------------------------------------------------ +// Thread Queries +//------------------------------------------------------------------ + +ThreadList & +Process::GetThreadList () +{ + return m_thread_list; +} + +const ThreadList & +Process::GetThreadList () const +{ + return m_thread_list; +} + + +bool +Process::StartPrivateStateThread () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); + + if (log) + log->Printf ("Process::%s ( )", __FUNCTION__); + + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + m_private_state_thread = Host::ThreadCreate ("<lldb.process.internal-state>", Process::PrivateStateThread, this, NULL); + return m_private_state_thread != LLDB_INVALID_HOST_THREAD; +} + +void +Process::PausePrivateStateThread () +{ + ControlPrivateStateThread (eBroadcastInternalStateControlPause); +} + +void +Process::ResumePrivateStateThread () +{ + ControlPrivateStateThread (eBroadcastInternalStateControlResume); +} + +void +Process::StopPrivateStateThread () +{ + ControlPrivateStateThread (eBroadcastInternalStateControlStop); +} + +void +Process::ControlPrivateStateThread (uint32_t signal) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS); + + assert (signal == eBroadcastInternalStateControlStop || + signal == eBroadcastInternalStateControlPause || + signal == eBroadcastInternalStateControlResume); + + if (log) + log->Printf ("Process::%s ( ) - signal: %d", __FUNCTION__, signal); + + // Signal the private state thread + if (m_private_state_thread != LLDB_INVALID_HOST_THREAD) + { + TimeValue timeout_time; + bool timed_out; + + m_private_state_control_broadcaster.BroadcastEvent (signal, NULL); + + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(2); + m_private_state_control_wait.WaitForValueEqualTo (true, &timeout_time, &timed_out); + m_private_state_control_wait.SetValue (false, eBroadcastNever); + + if (signal == eBroadcastInternalStateControlStop) + { + if (timed_out) + Host::ThreadCancel (m_private_state_thread, NULL); + + thread_result_t result = NULL; + Host::ThreadJoin (m_private_state_thread, &result, NULL); + } + } +} + +void +Process::HandlePrivateEvent (EventSP &event_sp) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + // See if we should broadcast this state to external clients? + const bool should_broadcast = ShouldBroadcastEvent (event_sp.get()); + if (log) + log->Printf ("Process::%s (arg = %p, pid = %i) got event '%s' broadcast = %s", __FUNCTION__, this, GetID(), StateAsCString(internal_state), should_broadcast ? "yes" : "no"); + + if (should_broadcast) + { + if (log) + { + log->Printf ("\tChanging public state from: %s to %s", StateAsCString(GetState ()), StateAsCString (internal_state)); + } + Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); + BroadcastEvent (event_sp); + } + else + { + if (log) + { + log->Printf ("\tNot changing public state with event: %s", StateAsCString (internal_state)); + } + } +} + +void * +Process::PrivateStateThread (void *arg) +{ + Process *proc = static_cast<Process*> (arg); + void *result = proc->RunPrivateStateThread (); + proc->m_private_state_thread = LLDB_INVALID_HOST_THREAD; + return result; +} + +void * +Process::RunPrivateStateThread () +{ + bool control_only = false; + m_private_state_control_wait.SetValue (false, eBroadcastNever); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("Process::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, this, GetID()); + + bool exit_now = false; + while (!exit_now) + { + EventSP event_sp; + WaitForEventsPrivate (NULL, event_sp, control_only); + if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) + { + switch (event_sp->GetType()) + { + case eBroadcastInternalStateControlStop: + exit_now = true; + continue; // Go to next loop iteration so we exit without + break; // doing any internal state managment below + + case eBroadcastInternalStateControlPause: + control_only = true; + break; + + case eBroadcastInternalStateControlResume: + control_only = false; + break; + } + m_private_state_control_wait.SetValue (true, eBroadcastAlways); + } + + + const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (internal_state != eStateInvalid) + { + HandlePrivateEvent (event_sp); + } + + if (internal_state == eStateInvalid || internal_state == eStateExited) + break; + } + + if (log) + log->Printf ("Process::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, this, GetID()); + + return NULL; +} + +addr_t +Process::GetSectionLoadAddress (const Section *section) const +{ + // TODO: add support for the same section having multiple load addresses + addr_t section_load_addr = LLDB_INVALID_ADDRESS; + if (m_section_load_info.GetFirstKeyForValue (section, section_load_addr)) + return section_load_addr; + return LLDB_INVALID_ADDRESS; +} + +bool +Process::SectionLoaded (const Section *section, addr_t load_addr) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_SHLIB | LIBLLDB_LOG_VERBOSE); + + if (log) + log->Printf ("Process::%s (section = %p (%s.%s), load_addr = 0x%16.16llx)", + __FUNCTION__, + section, + section->GetModule()->GetFileSpec().GetFilename().AsCString(), + section->GetName().AsCString(), + load_addr); + + + const Section *existing_section = NULL; + Mutex::Locker locker(m_section_load_info.GetMutex()); + + if (m_section_load_info.GetValueForKeyNoLock (load_addr, existing_section)) + { + if (existing_section == section) + return false; // No change + } + m_section_load_info.SetValueForKeyNoLock (load_addr, section); + return true; // Changed +} + +size_t +Process::SectionUnloaded (const Section *section) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_SHLIB | LIBLLDB_LOG_VERBOSE); + + if (log) + log->Printf ("Process::%s (section = %p (%s.%s))", + __FUNCTION__, + section, + section->GetModule()->GetFileSpec().GetFilename().AsCString(), + section->GetName().AsCString()); + + Mutex::Locker locker(m_section_load_info.GetMutex()); + + size_t unload_count = 0; + addr_t section_load_addr; + while (m_section_load_info.GetFirstKeyForValueNoLock (section, section_load_addr)) + { + unload_count += m_section_load_info.EraseNoLock (section_load_addr); + } + return unload_count; +} + +bool +Process::SectionUnloaded (const Section *section, addr_t load_addr) +{ + Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_SHLIB | LIBLLDB_LOG_VERBOSE); + + if (log) + log->Printf ("Process::%s (section = %p (%s.%s), load_addr = 0x%16.16llx)", + __FUNCTION__, + section, + section->GetModule()->GetFileSpec().GetFilename().AsCString(), + section->GetName().AsCString(), + load_addr); + + return m_section_load_info.Erase (load_addr) == 1; +} + + +bool +Process::ResolveLoadAddress (addr_t load_addr, Address &so_addr) const +{ + addr_t section_load_addr = LLDB_INVALID_ADDRESS; + const Section *section = NULL; + + // First find the top level section that this load address exists in + if (m_section_load_info.LowerBound (load_addr, section_load_addr, section, true)) + { + addr_t offset = load_addr - section_load_addr; + if (offset < section->GetByteSize()) + { + // We have found the top level section, now we need to find the + // deepest child section. + return section->ResolveContainedAddress (offset, so_addr); + } + } + so_addr.Clear(); + return false; +} + +//------------------------------------------------------------------ +// Process Event Data +//------------------------------------------------------------------ + +Process::ProcessEventData::ProcessEventData () : + EventData (), + m_process_sp (), + m_state (eStateInvalid), + m_update_state (false), + m_restarted (false) +{ +} + +Process::ProcessEventData::ProcessEventData (const ProcessSP &process_sp, StateType state) : + EventData (), + m_process_sp (process_sp), + m_state (state), + m_update_state (false), + m_restarted (false) +{ +} + +Process::ProcessEventData::~ProcessEventData() +{ +} + +const ConstString & +Process::ProcessEventData::GetFlavorString () +{ + static ConstString g_flavor ("Process::ProcessEventData"); + return g_flavor; +} + +const ConstString & +Process::ProcessEventData::GetFlavor () const +{ + return ProcessEventData::GetFlavorString (); +} + +const ProcessSP & +Process::ProcessEventData::GetProcessSP () const +{ + return m_process_sp; +} + +StateType +Process::ProcessEventData::GetState () const +{ + return m_state; +} + +bool +Process::ProcessEventData::GetRestarted () const +{ + return m_restarted; +} + +void +Process::ProcessEventData::SetRestarted (bool new_value) +{ + m_restarted = new_value; +} + +void +Process::ProcessEventData::DoOnRemoval (Event *event_ptr) +{ + // This function gets called twice for each event, once when the event gets pulled + // off of the private process event queue, and once when it gets pulled off of + // the public event queue. m_update_state is used to distinguish these + // two cases; it is false when we're just pulling it off for private handling, + // and we don't want to do the breakpoint command handling then. + + if (!m_update_state) + return; + + m_process_sp->SetPublicState (m_state); + + // If we're stopped and haven't restarted, then do the breakpoint commands here: + if (m_state == eStateStopped && ! m_restarted) + { + int num_threads = m_process_sp->GetThreadList().GetSize(); + int idx; + + for (idx = 0; idx < num_threads; ++idx) + { + lldb::ThreadSP thread_sp = m_process_sp->GetThreadList().GetThreadAtIndex(idx); + + Thread::StopInfo stop_info; + if (thread_sp->GetStopInfo(&stop_info)) + { + StopReason reason = stop_info.GetStopReason(); + if (reason == eStopReasonBreakpoint) + { + BreakpointSiteSP bp_site_sp; + // Look up the breakpoint site in the stop info, but the breakpoint + // might be a temporary one that's been deleted between the time we + // hit the breakpoint and now, if so there's nothing to do. + + bp_site_sp = m_process_sp->GetBreakpointSiteList().FindByID (stop_info.GetBreakpointSiteID()); + if (bp_site_sp) + { + size_t num_owners = bp_site_sp->GetNumberOfOwners(); + for (size_t j = 0; j < num_owners; j++) + { + lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j); + StoppointCallbackContext context (event_ptr, + m_process_sp.get(), + thread_sp.get(), + thread_sp->GetStackFrameAtIndex(0).get(), + false); + bp_loc_sp->InvokeCallback (&context); + } + } + else + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + + if (log) + log->Printf ("Process::%s could not find breakpoint site id: %d...", __FUNCTION__, stop_info.GetBreakpointSiteID()); + } + + } + } + } + if (m_process_sp->GetPrivateState() == eStateRunning) + SetRestarted(true); + } +} + +void +Process::ProcessEventData::Dump (Stream *s) const +{ + if (m_process_sp) + s->Printf(" process = %p (pid = %u), ", m_process_sp.get(), m_process_sp->GetID()); + + s->Printf("state = %s", StateAsCString(GetState()));; +} + +const Process::ProcessEventData * +Process::ProcessEventData::GetEventDataFromEvent (const Event *event_ptr) +{ + if (event_ptr) + { + const EventData *event_data = event_ptr->GetData(); + if (event_data && event_data->GetFlavor() == ProcessEventData::GetFlavorString()) + return static_cast <const ProcessEventData *> (event_ptr->GetData()); + } + return NULL; +} + +ProcessSP +Process::ProcessEventData::GetProcessFromEvent (const Event *event_ptr) +{ + ProcessSP process_sp; + const ProcessEventData *data = GetEventDataFromEvent (event_ptr); + if (data) + process_sp = data->GetProcessSP(); + return process_sp; +} + +StateType +Process::ProcessEventData::GetStateFromEvent (const Event *event_ptr) +{ + const ProcessEventData *data = GetEventDataFromEvent (event_ptr); + if (data == NULL) + return eStateInvalid; + else + return data->GetState(); +} + +bool +Process::ProcessEventData::GetRestartedFromEvent (const Event *event_ptr) +{ + const ProcessEventData *data = GetEventDataFromEvent (event_ptr); + if (data == NULL) + return false; + else + return data->GetRestarted(); +} + +void +Process::ProcessEventData::SetRestartedInEvent (Event *event_ptr, bool new_value) +{ + ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr)); + if (data != NULL) + data->SetRestarted(new_value); +} + +bool +Process::ProcessEventData::SetUpdateStateOnRemoval (Event *event_ptr) +{ + ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr)); + if (data) + { + data->SetUpdateStateOnRemoval(); + return true; + } + return false; +} + +void +Process::ProcessEventData::SetUpdateStateOnRemoval() +{ + m_update_state = true; +} + +Target * +Process::CalculateTarget () +{ + return &m_target; +} + +Process * +Process::CalculateProcess () +{ + return this; +} + +Thread * +Process::CalculateThread () +{ + return NULL; +} + +StackFrame * +Process::CalculateStackFrame () +{ + return NULL; +} + +void +Process::Calculate (ExecutionContext &exe_ctx) +{ + exe_ctx.target = &m_target; + exe_ctx.process = this; + exe_ctx.thread = NULL; + exe_ctx.frame = NULL; +} + +lldb::ProcessSP +Process::GetSP () +{ + return GetTarget().GetProcessSP(); +} + +ObjCObjectPrinter & +Process::GetObjCObjectPrinter() +{ + return m_objc_object_printer; +} + diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp new file mode 100644 index 00000000000..b1838ba477a --- /dev/null +++ b/lldb/source/Target/RegisterContext.cpp @@ -0,0 +1,238 @@ +//===-- RegisterContext.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/RegisterContext.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RegisterContext constructor +//---------------------------------------------------------------------- +RegisterContext::RegisterContext (Thread &thread, StackFrame *frame) : + m_thread (thread), + m_frame (frame) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RegisterContext::~RegisterContext() +{ +} + +const RegisterInfo * +RegisterContext::GetRegisterInfoByName (const char *reg_name, uint32_t start_idx) +{ + if (reg_name && reg_name[0]) + { + const uint32_t num_registers = GetRegisterCount(); + for (uint32_t reg = start_idx; reg < num_registers; ++reg) + { + const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); + + if ((reg_info->name != NULL && ::strcasecmp (reg_info->name, reg_name) == 0) || + (reg_info->alt_name != NULL && ::strcasecmp (reg_info->alt_name, reg_name) == 0)) + { + return reg_info; + } + } + } + return NULL; +} + +const char * +RegisterContext::GetRegisterName (uint32_t reg) +{ + const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info) + return reg_info->name; + return NULL; +} + +uint64_t +RegisterContext::GetPC(uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +bool +RegisterContext::SetPC(uint64_t pc) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + bool success = WriteRegisterFromUnsigned (reg, pc); + if (success) + { + if (m_frame) + m_frame->ChangePC(pc); + else + m_thread.ClearStackFrames (); + } + return success; +} + +uint64_t +RegisterContext::GetSP(uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +bool +RegisterContext::SetSP(uint64_t sp) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + return WriteRegisterFromUnsigned (reg, sp); +} + +uint64_t +RegisterContext::GetFP(uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +bool +RegisterContext::SetFP(uint64_t fp) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + return WriteRegisterFromUnsigned (reg, fp); +} + +uint64_t +RegisterContext::GetReturnAddress (uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + return ReadRegisterAsUnsigned (reg, fail_value); +} + +uint64_t +RegisterContext::GetFlags (uint64_t fail_value) +{ + uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return ReadRegisterAsUnsigned (reg, fail_value); +} + + +uint64_t +RegisterContext::ReadRegisterAsUnsigned (uint32_t reg, uint64_t fail_value) +{ + if (reg != LLDB_INVALID_REGNUM) + { + Scalar value; + if (ReadRegisterValue (reg, value)) + return value.GetRawBits64(fail_value); + } + return fail_value; +} + +bool +RegisterContext::WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval) +{ + if (reg == LLDB_INVALID_REGNUM) + return false; + Scalar value(uval); + return WriteRegisterValue (reg, value); +} + +lldb::tid_t +RegisterContext::GetThreadID() const +{ + return m_thread.GetID(); +} + +uint32_t +RegisterContext::NumSupportedHardwareBreakpoints () +{ + return 0; +} + +uint32_t +RegisterContext::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContext::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + return false; +} + + +uint32_t +RegisterContext::NumSupportedHardwareWatchpoints () +{ + return 0; +} + +uint32_t +RegisterContext::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write) +{ + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContext::ClearHardwareWatchpoint (uint32_t hw_index) +{ + return false; +} + +bool +RegisterContext::HardwareSingleStep (bool enable) +{ + return false; +} + +Target * +RegisterContext::CalculateTarget () +{ + return m_thread.CalculateTarget(); +} + + +Process * +RegisterContext::CalculateProcess () +{ + return m_thread.CalculateProcess (); +} + +Thread * +RegisterContext::CalculateThread () +{ + return &m_thread; +} + +StackFrame * +RegisterContext::CalculateStackFrame () +{ + return m_frame; +} + +void +RegisterContext::Calculate (ExecutionContext &exe_ctx) +{ + if (m_frame) + m_frame->Calculate (exe_ctx); + else + m_thread.Calculate (exe_ctx); +} + + + diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp new file mode 100644 index 00000000000..cb832951661 --- /dev/null +++ b/lldb/source/Target/StackFrame.cpp @@ -0,0 +1,393 @@ +//===-- StackFrame.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/StackFrame.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +// The first bits in the flags are reserved for the SymbolContext::Scope bits +// so we know if we have tried to look up information in our internal symbol +// context (m_sc) already. +#define RESOLVED_PC_SO_ADDR (uint32_t(eSymbolContextEverything + 1)) +#define RESOLVED_FRAME_ID (RESOLVED_PC_SO_ADDR << 1) +#define GOT_FRAME_BASE (RESOLVED_FRAME_ID << 1) +#define FRAME_IS_OBSOLETE (GOT_FRAME_BASE << 1) +#define RESOLVED_VARIABLES (FRAME_IS_OBSOLETE << 1) + +StackFrame::StackFrame (lldb::user_id_t frame_idx, Thread &thread, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr) : + UserID (frame_idx), + m_thread (thread), + m_reg_context_sp(), + m_id(cfa), + m_pc(NULL, pc), + m_sc(), + m_flags(), + m_frame_base(), + m_frame_base_error(), + m_variable_list_sp (), + m_value_object_list () +{ + if (sc_ptr != NULL) + m_sc = *sc_ptr; +} + +StackFrame::StackFrame (lldb::user_id_t frame_idx, Thread &thread, RegisterContextSP ®_context_sp, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr) : + UserID (frame_idx), + m_thread (thread), + m_reg_context_sp(reg_context_sp), + m_id(cfa), + m_pc(NULL, pc), + m_sc(), + m_flags(), + m_frame_base(), + m_frame_base_error(), + m_variable_list_sp (), + m_value_object_list () +{ + if (sc_ptr != NULL) + m_sc = *sc_ptr; +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StackFrame::~StackFrame() +{ +} + +StackID& +StackFrame::GetStackID() +{ + // Make sure we have resolved our stack ID's address range before we give + // it out to any external clients + if (m_id.GetStartAddress().IsValid() == 0 && m_flags.IsClear(RESOLVED_FRAME_ID)) + { + m_flags.Set (RESOLVED_FRAME_ID); + + // Resolve our PC to section offset if we haven't alreday done so + // and if we don't have a module. The resolved address section will + // contain the module to which it belongs. + if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_PC_SO_ADDR)) + GetPC(); + + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction; + + if (m_sc.module_sp) + { + if (m_sc.module_sp->ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextFunction) + { + assert (m_sc.function); + m_id.SetStartAddress(m_sc.function->GetAddressRange().GetBaseAddress()); + } + else if (m_sc.module_sp->ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextSymbol) + { + assert (m_sc.symbol); + AddressRange *symbol_range_ptr = m_sc.symbol->GetAddressRangePtr(); + if (symbol_range_ptr) + m_id.SetStartAddress(symbol_range_ptr->GetBaseAddress()); + } + } +// else if (m_sc.target != NULL) +// { +// if (m_sc.target->GetImages().ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextFunction) +// { +// assert (m_sc.function); +// m_id.GetAddressRange() = m_sc.function->GetAddressRange(); +// } +// else if (m_sc.target->GetImages().ResolveSymbolContextForAddress (GetPC(), resolve_scope, m_sc) & eSymbolContextSymbol) +// { +// assert (m_sc.symbol); +// AddressRange *symbol_range_ptr = m_sc.symbol->GetAddressRange(); +// if (symbol_range_ptr) +// m_id.GetAddressRange() = *symbol_range_ptr; +// } +// } + } + return m_id; +} + +Address& +StackFrame::GetPC() +{ + if (m_flags.IsClear(RESOLVED_PC_SO_ADDR) && !m_pc.IsSectionOffset()) + { + m_flags.Set (RESOLVED_PC_SO_ADDR); + + // Resolve the PC into a temporary address because if ResolveLoadAddress + // fails to resolve the address, it will clear the address object... + Address resolved_pc; + if (m_thread.GetProcess().ResolveLoadAddress(m_pc.GetOffset(), resolved_pc)) + { + m_pc = resolved_pc; + const Section *section = m_pc.GetSection(); + if (section) + { + Module *module = section->GetModule(); + if (module) + { + m_sc.module_sp = module->GetSP(); + if (m_sc.module_sp) + m_flags.Set(eSymbolContextModule); + } + } + } + } + return m_pc; +} + +void +StackFrame::ChangePC (addr_t pc) +{ + m_pc.SetOffset(pc); + m_pc.SetSection(NULL); + m_sc.Clear(); + m_flags.SetAllFlagBits(0); + m_thread.ClearStackFrames (); +} + +const char * +StackFrame::Disassemble () +{ + if (m_disassembly.GetSize() == 0) + { + ExecutionContext exe_ctx; + Calculate(exe_ctx); + Disassembler::Disassemble (m_thread.GetProcess().GetTarget().GetArchitecture(), + exe_ctx, + 0, + m_disassembly); + if (m_disassembly.GetSize() == 0) + return NULL; + } + return m_disassembly.GetData(); +} + +//---------------------------------------------------------------------- +// Get the symbol context if we already haven't done so by resolving the +// PC address as much as possible. This way when we pass around a +// StackFrame object, everyone will have as much information as +// possible and no one will ever have to look things up manually. +//---------------------------------------------------------------------- +const SymbolContext& +StackFrame::GetSymbolContext (uint32_t resolve_scope) +{ + // Copy our internal symbol context into "sc". + + if ((m_flags.GetAllFlagBits() & resolve_scope) != resolve_scope) + { + // Resolve our PC to section offset if we haven't alreday done so + // and if we don't have a module. The resolved address section will + // contain the module to which it belongs + if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_PC_SO_ADDR)) + GetPC(); + + // If this is not frame zero, then we need to subtract 1 from the PC + // value when doing address lookups since the PC will be on the + // instruction following the function call instruction... + + Address lookup_addr(GetPC()); + if (GetID() > 0 && lookup_addr.IsValid()) + { + addr_t offset = lookup_addr.GetOffset(); + if (offset > 0) + lookup_addr.SetOffset(offset - 1); + } + + if (m_sc.module_sp) + { + // We have something in our stack frame symbol context, lets check + // if we haven't already tried to lookup one of those things. If we + // haven't then we will do the query. + if ((m_sc.comp_unit == NULL && (resolve_scope & eSymbolContextCompUnit ) && m_flags.IsClear(eSymbolContextCompUnit )) || + (m_sc.function == NULL && (resolve_scope & eSymbolContextFunction ) && m_flags.IsClear(eSymbolContextFunction )) || + (m_sc.block == NULL && (resolve_scope & eSymbolContextBlock ) && m_flags.IsClear(eSymbolContextBlock )) || + (m_sc.symbol == NULL && (resolve_scope & eSymbolContextSymbol ) && m_flags.IsClear(eSymbolContextSymbol )) || + (!m_sc.line_entry.IsValid() && (resolve_scope & eSymbolContextLineEntry) && m_flags.IsClear(eSymbolContextLineEntry ))) + { + // We might be resolving less information than what is already + // in our current symbol context so resolve into a temporary + // symbol context "sc" so we don't clear out data we have + // already found in "m_sc" + SymbolContext sc; + // Set flags that indicate what we have tried to resolve + const uint32_t resolved = m_sc.module_sp->ResolveSymbolContextForAddress (lookup_addr, resolve_scope, sc); + if (resolved & eSymbolContextCompUnit) m_sc.comp_unit = sc.comp_unit; + if (resolved & eSymbolContextFunction) m_sc.function = sc.function; + if (resolved & eSymbolContextBlock) m_sc.block = sc.block; + if (resolved & eSymbolContextSymbol) m_sc.symbol = sc.symbol; + if (resolved & eSymbolContextLineEntry) m_sc.line_entry = sc.line_entry; + } + } + else + { + // If we don't have a module, then we can't have the compile unit, + // function, block, line entry or symbol, so we can safely call + // ResolveSymbolContextForAddress with our symbol context member m_sc. + m_thread.GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (lookup_addr, resolve_scope, m_sc); + } + + // If the target was requested add that: + if (m_sc.target_sp.get() == NULL) + m_sc.target_sp = CalculateProcess()->GetTarget().GetSP(); + + // Update our internal flags so we remember what we have tried to locate so + // we don't have to keep trying when more calls to this function are made. + m_flags.Set(resolve_scope); + } + + // Return the symbol context with everything that was possible to resolve + // resolved. + return m_sc; +} + + +VariableList * +StackFrame::GetVariableList () +{ + if (m_flags.IsClear(RESOLVED_VARIABLES)) + { + m_flags.Set(RESOLVED_VARIABLES); + + GetSymbolContext(eSymbolContextFunction); + if (m_sc.function) + { + bool get_child_variables = true; + bool can_create = true; + m_variable_list_sp = m_sc.function->GetBlocks(can_create).GetVariableList (Block::RootID, get_child_variables, can_create); + } + } + return m_variable_list_sp.get(); +} + + +bool +StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr) +{ + if (m_flags.IsClear(GOT_FRAME_BASE)) + { + if (m_sc.function) + { + m_frame_base.Clear(); + m_frame_base_error.Clear(); + + m_flags.Set(GOT_FRAME_BASE); + ExecutionContext exe_ctx (&m_thread.GetProcess(), &m_thread, this); + Value expr_value; + if (m_sc.function->GetFrameBaseExpression().Evaluate(&exe_ctx, NULL, NULL, expr_value, &m_frame_base_error) < 0) + { + // We should really have an error if evaluate returns, but in case + // we don't, lets set the error to something at least. + if (m_frame_base_error.Success()) + m_frame_base_error.SetErrorString("Evaluation of the frame base expression failed."); + } + else + { + m_frame_base = expr_value.ResolveValue(&exe_ctx, NULL); + } + } + else + { + m_frame_base_error.SetErrorString ("No function in symbol context."); + } + } + + if (m_frame_base_error.Success()) + frame_base = m_frame_base; + + if (error_ptr) + *error_ptr = m_frame_base_error; + return m_frame_base_error.Success(); +} + +RegisterContext * +StackFrame::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp.reset (m_thread.CreateRegisterContextForFrame (this)); + return m_reg_context_sp.get(); +} + +bool +StackFrame::HasDebugInformation () +{ + GetSymbolContext(eSymbolContextLineEntry); + return m_sc.line_entry.IsValid(); +} + +ValueObjectList & +StackFrame::GetValueObjectList() +{ + return m_value_object_list; +} + + +Target * +StackFrame::CalculateTarget () +{ + return m_thread.CalculateTarget(); +} + +Process * +StackFrame::CalculateProcess () +{ + return m_thread.CalculateProcess(); +} + +Thread * +StackFrame::CalculateThread () +{ + return &m_thread; +} + +StackFrame * +StackFrame::CalculateStackFrame () +{ + return this; +} + + +void +StackFrame::Calculate (ExecutionContext &exe_ctx) +{ + m_thread.Calculate (exe_ctx); + exe_ctx.frame = this; +} + +void +StackFrame::Dump (Stream *strm, bool show_frame_index) +{ + if (strm == NULL) + return; + + if (show_frame_index) + strm->Printf("frame #%u: ", GetID()); + strm->Printf("pc = 0x%0*llx", m_thread.GetProcess().GetAddressByteSize() * 2, GetRegisterContext()->GetPC()); + SymbolContext sc (GetSymbolContext(eSymbolContextEverything)); + strm->PutCString(", where = "); + sc.DumpStopContext(strm, &m_thread.GetProcess(), GetPC()); +} + diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp new file mode 100644 index 00000000000..8615f714673 --- /dev/null +++ b/lldb/source/Target/StackFrameList.cpp @@ -0,0 +1,135 @@ +//===-- StackFrameList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/StackFrameList.h" +#include "lldb/Target/StackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StackFrameList constructor +//---------------------------------------------------------------------- +StackFrameList::StackFrameList() : + m_mutex (Mutex::eMutexTypeRecursive), + m_frames (), + m_current_frame_idx (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StackFrameList::~StackFrameList() +{ +} + + +uint32_t +StackFrameList::GetNumFrames() const +{ + Mutex::Locker locker (m_mutex); + return m_frames.size(); +} + +// After we have determined the number of frames, we can set the count here +// and have the frame info be generated on demand. +void +StackFrameList::SetNumFrames(uint32_t count) +{ + Mutex::Locker locker (m_mutex); + return m_frames.resize(count); +} + +StackFrameSP +StackFrameList::GetFrameAtIndex (uint32_t idx) const +{ + StackFrameSP frame_sp; + { + Mutex::Locker locker (m_mutex); + if (idx < m_frames.size()) + frame_sp = m_frames[idx]; + } + return frame_sp; +} + +bool +StackFrameList::SetFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp) +{ + Mutex::Locker locker (m_mutex); + if (idx >= m_frames.size()) + m_frames.resize(idx + 1); + // Make sure allocation succeeded by checking bounds again + if (idx < m_frames.size()) + { + m_frames[idx] = frame_sp; + return true; + } + return false; // resize failed, out of memory? +} + +uint32_t +StackFrameList::GetCurrentFrameIndex () const +{ + Mutex::Locker locker (m_mutex); + return m_current_frame_idx; +} + + +uint32_t +StackFrameList::SetCurrentFrame (lldb_private::StackFrame *frame) +{ + Mutex::Locker locker (m_mutex); + const_iterator pos, + begin = m_frames.begin(), + end = m_frames.end(); + for (pos = begin; pos != end; ++pos) + { + if (pos->get() == frame) + { + m_current_frame_idx = std::distance (begin, pos); + return m_current_frame_idx; + } + } + m_current_frame_idx = 0; + return m_current_frame_idx; +} + +// Mark a stack frame as the current frame using the frame index +void +StackFrameList::SetCurrentFrameByIndex (uint32_t idx) +{ + Mutex::Locker locker (m_mutex); + m_current_frame_idx = idx; +} + +// The thread has been run, reset the number stack frames to zero so we can +// determine how many frames we have lazily. +void +StackFrameList::Clear () +{ + Mutex::Locker locker (m_mutex); + m_frames.clear(); +} + +void +StackFrameList::InvalidateFrames (uint32_t start_idx) +{ + Mutex::Locker locker (m_mutex); + size_t num_frames = m_frames.size(); + while (start_idx < num_frames) + { + m_frames[start_idx].reset(); + ++start_idx; + } +} diff --git a/lldb/source/Target/StackID.cpp b/lldb/source/Target/StackID.cpp new file mode 100644 index 00000000000..6f903aed6da --- /dev/null +++ b/lldb/source/Target/StackID.cpp @@ -0,0 +1,110 @@ +//===-- StackID.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/StackID.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StackID constructor +//---------------------------------------------------------------------- +StackID::StackID() : + m_start_address(), + m_cfa() +{ +} + +//---------------------------------------------------------------------- +// StackID constructor with args +//---------------------------------------------------------------------- +StackID::StackID (const Address& start_address, lldb::addr_t cfa) : + m_start_address (start_address), + m_cfa (cfa) +{ +} + +StackID::StackID (lldb::addr_t cfa) : + m_start_address (), + m_cfa (cfa) +{ +} + +//---------------------------------------------------------------------- +// StackID copy constructor +//---------------------------------------------------------------------- +StackID::StackID(const StackID& rhs) : + m_start_address (rhs.m_start_address), + m_cfa (rhs.m_cfa) +{ +} + +//---------------------------------------------------------------------- +// StackID assignment operator +//---------------------------------------------------------------------- +const StackID& +StackID::operator=(const StackID& rhs) +{ + if (this != &rhs) + { + m_start_address = rhs.m_start_address; + m_cfa = rhs.m_cfa; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StackID::~StackID() +{ +} + + +const Address& +StackID::GetStartAddress() const +{ + return m_start_address; +} + +void +StackID::SetStartAddress(const Address& start_address) +{ + m_start_address = start_address; +} + +lldb::addr_t +StackID::GetCallFrameAddress() const +{ + return m_cfa; +} + + +bool +lldb_private::operator== (const StackID& lhs, const StackID& rhs) +{ + return lhs.GetCallFrameAddress() == rhs.GetCallFrameAddress() && lhs.GetStartAddress() == rhs.GetStartAddress(); +} + +bool +lldb_private::operator!= (const StackID& lhs, const StackID& rhs) +{ + return lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress() || lhs.GetStartAddress() != rhs.GetStartAddress(); +} + +bool +lldb_private::operator< (const StackID& lhs, const StackID& rhs) +{ + return lhs.GetCallFrameAddress() < rhs.GetCallFrameAddress(); +} + diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp new file mode 100644 index 00000000000..48f2ea70fd0 --- /dev/null +++ b/lldb/source/Target/Target.cpp @@ -0,0 +1,707 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Target.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverAddress.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Breakpoint/BreakpointResolverName.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/Debugger.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Target constructor +//---------------------------------------------------------------------- +Target::Target() : + Broadcaster("Target"), + m_images(), + m_breakpoint_list (false), + m_internal_breakpoint_list (true), + m_process_sp(), + m_triple(), + m_search_filter_sp(), + m_image_search_paths (ImageSearchPathsChanged, this), + m_scratch_ast_context_ap(NULL) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Target::Target()", this); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Target::~Target() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Target::~Target()", this); + DeleteCurrentProcess (); +} + +void +Target::Dump (Stream *s) +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->PutCString("Target\n"); + s->IndentMore(); + m_images.Dump(s); + m_breakpoint_list.Dump(s); + m_internal_breakpoint_list.Dump(s); +// if (m_process_sp.get()) +// m_process_sp->Dump(s); + s->IndentLess(); +} + +void +Target::DeleteCurrentProcess () +{ + if (m_process_sp.get()) + { + if (m_process_sp->IsAlive()) + m_process_sp->Destroy(); + else + m_process_sp->Finalize(); + + // Do any cleanup of the target we need to do between process instances. + // NB It is better to do this before destroying the process in case the + // clean up needs some help from the process. + m_breakpoint_list.ClearAllBreakpointSites(); + m_internal_breakpoint_list.ClearAllBreakpointSites(); + m_process_sp.reset(); + } +} + +const lldb::ProcessSP & +Target::CreateProcess (Listener &listener, const char *plugin_name) +{ + DeleteCurrentProcess (); + m_process_sp.reset(Process::FindPlugin(*this, plugin_name, listener)); + return m_process_sp; +} + +const lldb::ProcessSP & +Target::GetProcessSP () const +{ + return m_process_sp; +} + +lldb::TargetSP +Target::GetSP() +{ + return Debugger::GetSharedInstance().GetTargetList().GetTargetSP(this); +} + +BreakpointList & +Target::GetBreakpointList(bool internal) +{ + if (internal) + return m_internal_breakpoint_list; + else + return m_breakpoint_list; +} + +const BreakpointList & +Target::GetBreakpointList(bool internal) const +{ + if (internal) + return m_internal_breakpoint_list; + else + return m_breakpoint_list; +} + +BreakpointSP +Target::GetBreakpointByID (break_id_t break_id) +{ + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); + + return bp_sp; +} + +BreakpointSP +Target::CreateBreakpoint (const FileSpec *containingModule, const FileSpec &file, uint32_t line_no, bool check_inlines, bool internal) +{ + SearchFilterSP filter_sp(GetSearchFilterForModule (containingModule)); + BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine (NULL, file, line_no, check_inlines)); + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + + +BreakpointSP +Target::CreateBreakpoint (lldb::addr_t load_addr, bool internal) +{ + BreakpointSP bp_sp; + Address so_addr; + // Attempt to resolve our load address if possible, though it is ok if + // it doesn't resolve to section/offset. + + Process *process = GetProcessSP().get(); + if (process && process->ResolveLoadAddress(load_addr, so_addr)) + bp_sp = CreateBreakpoint(so_addr, internal); + return bp_sp; +} + +BreakpointSP +Target::CreateBreakpoint (Address &addr, bool internal) +{ + TargetSP target_sp = this->GetSP(); + SearchFilterSP filter_sp(new SearchFilter (target_sp)); + BreakpointResolverSP resolver_sp (new BreakpointResolverAddress (NULL, addr)); + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + +BreakpointSP +Target::CreateBreakpoint (FileSpec *containingModule, const char *func_name, bool internal) +{ + SearchFilterSP filter_sp(GetSearchFilterForModule (containingModule)); + BreakpointResolverSP resolver_sp (new BreakpointResolverName (NULL, func_name)); + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + + +SearchFilterSP +Target::GetSearchFilterForModule (const FileSpec *containingModule) +{ + SearchFilterSP filter_sp; + lldb::TargetSP target_sp = this->GetSP(); + if (containingModule != NULL) + { + // TODO: We should look into sharing module based search filters + // across many breakpoints like we do for the simple target based one + filter_sp.reset (new SearchFilterByModule (target_sp, *containingModule)); + } + else + { + if (m_search_filter_sp.get() == NULL) + m_search_filter_sp.reset (new SearchFilter (target_sp)); + filter_sp = m_search_filter_sp; + } + return filter_sp; +} + +BreakpointSP +Target::CreateBreakpoint (FileSpec *containingModule, RegularExpression &func_regex, bool internal) +{ + SearchFilterSP filter_sp(GetSearchFilterForModule (containingModule)); + BreakpointResolverSP resolver_sp(new BreakpointResolverName (NULL, func_regex)); + + return CreateBreakpoint (filter_sp, resolver_sp, internal); +} + +BreakpointSP +Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal) +{ + BreakpointSP bp_sp; + if (filter_sp && resolver_sp) + { + bp_sp.reset(new Breakpoint (*this, filter_sp, resolver_sp)); + resolver_sp->SetBreakpoint (bp_sp.get()); + + if (internal) + m_internal_breakpoint_list.Add (bp_sp); + else + m_breakpoint_list.Add (bp_sp); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + { + StreamString s; + bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, internal ? "yes" : "no", s.GetData()); + } + + // Broadcast the breakpoint creation event. + if (!internal && EventTypeHasListeners(eBroadcastBitBreakpointChanged)) + { + BroadcastEvent (eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (Breakpoint::BreakpointEventData::eBreakpointAdded, bp_sp)); + } + + bp_sp->ResolveBreakpoint(); + } + return bp_sp; +} + +void +Target::RemoveAllBreakpoints (bool internal_also) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); + + m_breakpoint_list.RemoveAll(); + if (internal_also) + m_internal_breakpoint_list.RemoveAll(); +} + +void +Target::DisableAllBreakpoints (bool internal_also) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); + + m_breakpoint_list.SetEnabledAll (false); + if (internal_also) + m_internal_breakpoint_list.SetEnabledAll (false); +} + +void +Target::EnableAllBreakpoints (bool internal_also) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); + + m_breakpoint_list.SetEnabledAll (true); + if (internal_also) + m_internal_breakpoint_list.SetEnabledAll (true); +} + +bool +Target::RemoveBreakpointByID (break_id_t break_id) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); + + if (DisableBreakpointByID (break_id)) + { + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + m_internal_breakpoint_list.Remove(break_id); + else + m_breakpoint_list.Remove(break_id); + return true; + } + return false; +} + +bool +Target::DisableBreakpointByID (break_id_t break_id) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); + + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); + if (bp_sp) + { + bp_sp->SetEnabled (false); + return true; + } + return false; +} + +bool +Target::EnableBreakpointByID (break_id_t break_id) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Printf ("Target::%s (break_id = %i, internal = %s)\n", + __FUNCTION__, + break_id, + LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); + + BreakpointSP bp_sp; + + if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) + bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); + else + bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); + + if (bp_sp) + { + bp_sp->SetEnabled (true); + return true; + } + return false; +} + +ModuleSP +Target::GetExecutableModule () +{ + ModuleSP executable_sp; + if (m_images.GetSize() > 0) + executable_sp = m_images.GetModuleAtIndex(0); + return executable_sp; +} + +void +Target::SetExecutableModule (ModuleSP& executable_sp, bool get_dependent_files) +{ + m_images.Clear(); + m_scratch_ast_context_ap.reset(); + + if (executable_sp.get()) + { + Timer scoped_timer (__PRETTY_FUNCTION__, + "Target::SetExecutableModule (executable = '%s/%s')", + executable_sp->GetFileSpec().GetDirectory().AsCString(), + executable_sp->GetFileSpec().GetFilename().AsCString()); + + m_images.Append(executable_sp); // The first image is our exectuable file + + ArchSpec exe_arch = executable_sp->GetArchitecture(); + FileSpecList dependent_files; + ObjectFile * executable_objfile = executable_sp->GetObjectFile(); + if (executable_objfile == NULL) + { + + FileSpec bundle_executable(executable_sp->GetFileSpec()); + if (Host::ResolveExecutableInBundle (&bundle_executable)) + { + ModuleSP bundle_exe_module_sp(GetSharedModule(bundle_executable, + exe_arch)); + SetExecutableModule (bundle_exe_module_sp, get_dependent_files); + if (bundle_exe_module_sp->GetObjectFile() != NULL) + executable_sp = bundle_exe_module_sp; + return; + } + } + + if (executable_objfile) + { + executable_objfile->GetDependentModules(dependent_files); + for (uint32_t i=0; i<dependent_files.GetSize(); i++) + { + ModuleSP image_module_sp(GetSharedModule(dependent_files.GetFileSpecPointerAtIndex(i), + exe_arch)); + if (image_module_sp.get()) + { + //image_module_sp->Dump(&s);// REMOVE THIS, DEBUG ONLY + ObjectFile *objfile = image_module_sp->GetObjectFile(); + if (objfile) + objfile->GetDependentModules(dependent_files); + } + } + } + + // Now see if we know the target triple, and if so, create our scratch AST context: + ConstString target_triple; + if (GetTargetTriple(target_triple)) + { + m_scratch_ast_context_ap.reset (new ClangASTContext(target_triple.GetCString())); + } + } +} + + +ModuleList& +Target::GetImages () +{ + return m_images; +} + +ArchSpec +Target::GetArchitecture () const +{ + ArchSpec arch; + if (m_images.GetSize() > 0) + { + Module *exe_module = m_images.GetModulePointerAtIndex(0); + if (exe_module) + arch = exe_module->GetArchitecture(); + } + return arch; +} + + + +bool +Target::GetTargetTriple(ConstString &triple) +{ + triple.Clear(); + + if (m_triple) + { + triple = m_triple; + } + else + { + Module *exe_module = GetExecutableModule().get(); + if (exe_module) + { + ObjectFile *objfile = exe_module->GetObjectFile(); + if (objfile) + { + objfile->GetTargetTriple(m_triple); + triple = m_triple; + } + } + } + return !triple.IsEmpty(); +} + +void +Target::ModuleAdded (ModuleSP &module_sp) +{ + // A module is being added to this target for the first time + ModuleList module_list; + module_list.Append(module_sp); + ModulesDidLoad (module_list); +} + +void +Target::ModuleUpdated (ModuleSP &old_module_sp, ModuleSP &new_module_sp) +{ + // A module is being added to this target for the first time + ModuleList module_list; + module_list.Append (old_module_sp); + ModulesDidUnload (module_list); + module_list.Clear (); + module_list.Append (new_module_sp); + ModulesDidLoad (module_list); +} + +void +Target::ModulesDidLoad (ModuleList &module_list) +{ + m_breakpoint_list.UpdateBreakpoints (module_list, true); + // TODO: make event data that packages up the module_list + BroadcastEvent (eBroadcastBitModulesLoaded, NULL); +} + +void +Target::ModulesDidUnload (ModuleList &module_list) +{ + m_breakpoint_list.UpdateBreakpoints (module_list, false); + // TODO: make event data that packages up the module_list + BroadcastEvent (eBroadcastBitModulesUnloaded, NULL); +} + +size_t +Target::ReadMemory +( + lldb::AddressType addr_type, + lldb::addr_t addr, + void *dst, + size_t dst_len, + Error &error, + ObjectFile* objfile +) +{ + size_t bytes_read = 0; + error.Clear(); + switch (addr_type) + { + case eAddressTypeFile: + if (objfile) + { + if (m_process_sp.get()) + { + // If we have an execution context with a process, lets try and + // resolve the file address in "objfile" and read it from the + // process + Address so_addr(addr, objfile->GetSectionList()); + lldb::addr_t load_addr = so_addr.GetLoadAddress(m_process_sp.get()); + if (load_addr == LLDB_INVALID_ADDRESS) + { + if (objfile->GetFileSpec()) + error.SetErrorStringWithFormat("0x%llx can't be resolved, %s in not currently loaded.\n", addr, objfile->GetFileSpec().GetFilename().AsCString()); + else + error.SetErrorStringWithFormat("0x%llx can't be resolved.\n", addr, objfile->GetFileSpec().GetFilename().AsCString()); + } + else + { + bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error); + if (bytes_read != dst_len) + { + if (error.Success()) + { + if (bytes_read == 0) + error.SetErrorStringWithFormat("Read memory from 0x%llx failed.\n", load_addr); + else + error.SetErrorStringWithFormat("Only %zu of %zu bytes were read from memory at 0x%llx.\n", bytes_read, dst_len, load_addr); + } + } + } + } + else + { + // Try and read the file based address from the object file's + // section data. + } + } + break; + + case eAddressTypeLoad: + if (m_process_sp.get()) + { + bytes_read = m_process_sp->ReadMemory(addr, dst, dst_len, error); + if (bytes_read != dst_len) + { + if (error.Success()) + { + if (bytes_read == 0) + error.SetErrorStringWithFormat("Read memory from 0x%llx failed.\n", addr); + else + error.SetErrorStringWithFormat("Only %zu of %zu bytes were read from memory at 0x%llx.\n", bytes_read, dst_len, addr); + } + } + } + else + error.SetErrorStringWithFormat("Need valid process to read load address.\n"); + break; + + case eAddressTypeHost: + // The address is an address in this process, so just copy it + ::memcpy (dst, (uint8_t*)NULL + addr, dst_len); + break; + + default: + error.SetErrorStringWithFormat ("Unsupported lldb::AddressType value (%i).\n", addr_type); + break; + } + return bytes_read; +} + + +ModuleSP +Target::GetSharedModule +( + const FileSpec& file_spec, + const ArchSpec& arch, + const UUID *uuid_ptr, + const ConstString *object_name, + off_t object_offset, + Error *error_ptr +) +{ + // Don't pass in the UUID so we can tell if we have a stale value in our list + ModuleSP old_module_sp; // This will get filled in if we have a new version of the library + bool did_create_module = false; + ModuleSP module_sp; + + // If there are image search path entries, try to use them first to acquire a suitable image. + + Error error; + + if (m_image_search_paths.GetSize()) + { + FileSpec transformed_spec; + if (m_image_search_paths.RemapPath (file_spec.GetDirectory(), transformed_spec.GetDirectory())) + { + transformed_spec.GetFilename() = file_spec.GetFilename(); + error = ModuleList::GetSharedModule (transformed_spec, arch, uuid_ptr, object_name, object_offset, module_sp, &old_module_sp, &did_create_module); + } + } + + // If a module hasn't been found yet, use the unmodified path. + + if (!module_sp) + { + error = (ModuleList::GetSharedModule (file_spec, arch, uuid_ptr, object_name, object_offset, module_sp, &old_module_sp, &did_create_module)); + } + + if (module_sp) + { + m_images.Append (module_sp); + if (did_create_module) + { + if (old_module_sp && m_images.GetIndexForModule (old_module_sp.get()) != LLDB_INVALID_INDEX32) + ModuleUpdated(old_module_sp, module_sp); + else + ModuleAdded(module_sp); + } + } + if (error_ptr) + *error_ptr = error; + return module_sp; +} + + +Target * +Target::CalculateTarget () +{ + return this; +} + +Process * +Target::CalculateProcess () +{ + return NULL; +} + +Thread * +Target::CalculateThread () +{ + return NULL; +} + +StackFrame * +Target::CalculateStackFrame () +{ + return NULL; +} + +void +Target::Calculate (ExecutionContext &exe_ctx) +{ + exe_ctx.target = this; + exe_ctx.process = NULL; // Do NOT fill in process... + exe_ctx.thread = NULL; + exe_ctx.frame = NULL; +} + +PathMappingList & +Target::GetImageSearchPathList () +{ + return m_image_search_paths; +} + +void +Target::ImageSearchPathsChanged +( + const PathMappingList &path_list, + void *baton +) +{ + Target *target = (Target *)baton; + if (target->m_images.GetSize() > 1) + { + ModuleSP exe_module_sp (target->GetExecutableModule()); + if (exe_module_sp) + { + target->m_images.Clear(); + target->SetExecutableModule (exe_module_sp, true); + } + } +} + +ClangASTContext * +Target::GetScratchClangASTContext() +{ + return m_scratch_ast_context_ap.get(); +} diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp new file mode 100644 index 00000000000..d92940003d6 --- /dev/null +++ b/lldb/source/Target/TargetList.cpp @@ -0,0 +1,342 @@ +//===-- TargetList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// TargetList constructor +//---------------------------------------------------------------------- +TargetList::TargetList() : + Broadcaster("TargetList"), + m_target_list(), + m_target_list_mutex (Mutex::eMutexTypeRecursive), + m_current_target_idx (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TargetList::~TargetList() +{ + Mutex::Locker locker(m_target_list_mutex); + m_target_list.clear(); +} + +Error +TargetList::CreateTarget +( + const FileSpec& file, + const ArchSpec& arch, + const UUID *uuid_ptr, + bool get_dependent_files, + TargetSP &target_sp +) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "TargetList::CreateTarget (file = '%s/%s', arch = '%s', uuid = %p)", + file.GetDirectory().AsCString(), + file.GetFilename().AsCString(), + arch.AsCString(), + uuid_ptr); + ModuleSP exe_module_sp; + FileSpec resolved_file(file); + if (!Host::ResolveExecutableInBundle (&resolved_file)) + resolved_file = file; + + Error error = ModuleList::GetSharedModule(resolved_file, + arch, + uuid_ptr, + NULL, + 0, + exe_module_sp, + NULL, + NULL); + if (exe_module_sp) + { + target_sp.reset(new Target); + target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); + + if (target_sp.get()) + { + Mutex::Locker locker(m_target_list_mutex); + m_current_target_idx = m_target_list.size(); + m_target_list.push_back(target_sp); + } + +// target_sp.reset(new Target); +// // Let the target resolve any funky bundle paths before we try and get +// // the object file... +// target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); +// if (exe_module_sp->GetObjectFile() == NULL) +// { +// error.SetErrorStringWithFormat("%s%s%s: doesn't contain architecture %s", +// file.GetDirectory().AsCString(), +// file.GetDirectory() ? "/" : "", +// file.GetFilename().AsCString(), +// arch.AsCString()); +// } +// else +// { +// if (target_sp.get()) +// { +// error.Clear(); +// Mutex::Locker locker(m_target_list_mutex); +// m_current_target_idx = m_target_list.size(); +// m_target_list.push_back(target_sp); +// } +// } + } + else + { + target_sp.reset(); + } + + return error; +} + +bool +TargetList::DeleteTarget (TargetSP &target_sp) +{ + Mutex::Locker locker(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + + for (pos = m_target_list.begin(); pos != end; ++pos) + { + if (pos->get() == target_sp.get()) + { + m_target_list.erase(pos); + return true; + } + } + return false; +} + + +TargetSP +TargetList::FindTargetWithExecutableAndArchitecture +( + const FileSpec &exe_file_spec, + const ArchSpec *exe_arch_ptr +) const +{ + Mutex::Locker locker (m_target_list_mutex); + TargetSP target_sp; + bool full_match = exe_file_spec.GetDirectory(); + + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + ModuleSP module_sp ((*pos)->GetExecutableModule()); + + if (module_sp) + { + if (FileSpec::Equal (exe_file_spec, module_sp->GetFileSpec(), full_match)) + { + if (exe_arch_ptr) + { + if (*exe_arch_ptr != module_sp->GetArchitecture()) + continue; + } + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP +TargetList::FindTargetWithProcessID (lldb::pid_t pid) const +{ + Mutex::Locker locker(m_target_list_mutex); + TargetSP target_sp; + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + Process* process = (*pos)->GetProcessSP().get(); + if (process && process->GetID() == pid) + { + target_sp = *pos; + break; + } + } + return target_sp; +} + + +TargetSP +TargetList::FindTargetWithProcess (Process *process) const +{ + TargetSP target_sp; + if (process) + { + Mutex::Locker locker(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + if (process == (*pos)->GetProcessSP().get()) + { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +TargetSP +TargetList::GetTargetSP (Target *target) const +{ + TargetSP target_sp; + if (target) + { + Mutex::Locker locker(m_target_list_mutex); + collection::const_iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + if (target == (*pos).get()) + { + target_sp = *pos; + break; + } + } + } + return target_sp; +} + +uint32_t +TargetList::SendAsyncInterrupt (lldb::pid_t pid) +{ + uint32_t num_async_interrupts_sent = 0; + + if (pid != LLDB_INVALID_PROCESS_ID) + { + TargetSP target_sp(FindTargetWithProcessID (pid)); + if (target_sp.get()) + { + Process* process = target_sp->GetProcessSP().get(); + if (process) + { + process->BroadcastEvent (Process::eBroadcastBitInterrupt, NULL); + ++num_async_interrupts_sent; + } + } + } + else + { + // We don't have a valid pid to broadcast to, so broadcast to the target + // list's async broadcaster... + BroadcastEvent (Process::eBroadcastBitInterrupt, NULL); + } + + return num_async_interrupts_sent; +} + +uint32_t +TargetList::SignalIfRunning (lldb::pid_t pid, int signo) +{ + uint32_t num_signals_sent = 0; + Process *process = NULL; + if (pid == LLDB_INVALID_PROCESS_ID) + { + // Signal all processes with signal + Mutex::Locker locker(m_target_list_mutex); + collection::iterator pos, end = m_target_list.end(); + for (pos = m_target_list.begin(); pos != end; ++pos) + { + process = (*pos)->GetProcessSP().get(); + if (process) + { + if (process->IsAlive()) + { + ++num_signals_sent; + process->Signal (signo); + } + } + } + } + else + { + // Signal a specific process with signal + TargetSP target_sp(FindTargetWithProcessID (pid)); + if (target_sp.get()) + { + process = target_sp->GetProcessSP().get(); + if (process) + { + if (process->IsAlive()) + { + ++num_signals_sent; + process->Signal (signo); + } + } + } + } + return num_signals_sent; +} + +int +TargetList::GetNumTargets () const +{ + Mutex::Locker locker (m_target_list_mutex); + return m_target_list.size(); +} + +lldb::TargetSP +TargetList::GetTargetAtIndex (uint32_t idx) const +{ + TargetSP target_sp; + Mutex::Locker locker (m_target_list_mutex); + if (idx < m_target_list.size()) + target_sp = m_target_list[idx]; + return target_sp; +} + +uint32_t +TargetList::SetCurrentTarget (Target* target) +{ + Mutex::Locker locker (m_target_list_mutex); + collection::const_iterator pos, + begin = m_target_list.begin(), + end = m_target_list.end(); + for (pos = begin; pos != end; ++pos) + { + if (pos->get() == target) + { + m_current_target_idx = std::distance (begin, pos); + return m_current_target_idx; + } + } + m_current_target_idx = 0; + return m_current_target_idx; +} + +lldb::TargetSP +TargetList::GetCurrentTarget () +{ + Mutex::Locker locker (m_target_list_mutex); + if (m_current_target_idx >= m_target_list.size()) + m_current_target_idx = 0; + return GetTargetAtIndex (m_current_target_idx); +} diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp new file mode 100644 index 00000000000..2f852cc3eae --- /dev/null +++ b/lldb/source/Target/Thread.cpp @@ -0,0 +1,1121 @@ +//===-- Thread.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Target/ThreadPlanContinue.h" +#include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepOverBreakpoint.h" +#include "lldb/Target/ThreadPlanStepThrough.h" +#include "lldb/Target/ThreadPlanStepInRange.h" +#include "lldb/Target/ThreadPlanStepOverRange.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepUntil.h" + +using namespace lldb; +using namespace lldb_private; + +Thread::Thread (Process &process, lldb::tid_t tid) : + UserID (tid), + m_index_id (process.GetNextThreadIndexID ()), + m_reg_context_sp (), + m_process (process), + m_state (eStateUnloaded), + m_plan_stack (), + m_immediate_plan_stack(), + m_completed_plan_stack(), + m_state_mutex (Mutex::eMutexTypeRecursive), + m_frames (), + m_current_frame_idx (0), + m_resume_signal (LLDB_INVALID_SIGNAL_NUMBER), + m_resume_state (eStateRunning) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Thread::Thread(tid = 0x%4.4x)", this, GetID()); + + QueueFundamentalPlan(true); +} + + +Thread::~Thread() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Thread::~Thread(tid = 0x%4.4x)", this, GetID()); +} + +int +Thread::GetResumeSignal () const +{ + return m_resume_signal; +} + +void +Thread::SetResumeSignal (int signal) +{ + m_resume_signal = signal; +} + +StateType +Thread::GetResumeState () const +{ + return m_resume_state; +} + +void +Thread::SetResumeState (StateType state) +{ + m_resume_state = state; +} + +Thread::StopInfo::StopInfo(Thread *thread) : + m_reason (eStopReasonInvalid), + m_description (), + m_thread (thread), + m_details () +{ + m_description[0] = '\0'; +} + +Thread::StopInfo::~StopInfo() +{ +} + + +void +Thread::StopInfo::Clear() +{ + m_reason = eStopReasonInvalid; + m_completed_plan_sp.reset(); + m_description[0] = '\0'; + ::bzero (&m_details, sizeof(m_details)); +} + +StopReason +Thread::StopInfo::GetStopReason() const +{ + return m_reason; +} + +const char * +Thread::StopInfo::GetStopDescription() const +{ + if (m_description[0]) + return m_description; + return NULL; +} + +void +Thread::StopInfo::SetStopDescription(const char *desc) +{ + if (desc && desc[0]) + { + ::snprintf (m_description, sizeof(m_description), "%s", desc); + } + else + { + m_description[0] = '\0'; + } +} + +void +Thread::StopInfo::SetThread (Thread* thread) +{ + m_thread = thread; +} + +Thread * +Thread::StopInfo::GetThread () +{ + return m_thread; +} + +lldb::user_id_t +Thread::StopInfo::GetBreakpointSiteID() const +{ + if (m_reason == eStopReasonBreakpoint) + return m_details.breakpoint.bp_site_id; + return LLDB_INVALID_BREAK_ID; +} + +void +Thread::StopInfo::SetStopReasonWithBreakpointSiteID (lldb::user_id_t bp_site_id) +{ + m_reason = eStopReasonBreakpoint; + m_details.breakpoint.bp_site_id = bp_site_id; +} + +lldb::user_id_t +Thread::StopInfo::GetWatchpointID() const +{ + if (m_reason == eStopReasonWatchpoint) + return m_details.watchpoint.watch_id; + return LLDB_INVALID_WATCH_ID; +} + +void +Thread::StopInfo::SetStopReasonWithWatchpointID (lldb::user_id_t watch_id) +{ + m_reason = eStopReasonWatchpoint; + m_details.watchpoint.watch_id = watch_id; +} + + +int +Thread::StopInfo::GetSignal() const +{ + if (m_reason == eStopReasonSignal) + return m_details.signal.signo; + return 0; +} + +lldb::user_id_t +Thread::StopInfo::GetPlanID() const +{ + if (m_reason == eStopReasonPlanComplete) + return m_completed_plan_sp->GetID(); + return LLDB_INVALID_UID; +} + +void +Thread::StopInfo::SetStopReasonWithSignal (int signo) +{ + m_reason = eStopReasonSignal; + m_details.signal.signo = signo; +} + +void +Thread::StopInfo::SetStopReasonToTrace () +{ + m_reason = eStopReasonTrace; +} + +uint32_t +Thread::StopInfo::GetExceptionType() const +{ + if (m_reason == eStopReasonException) + return m_details.exception.type; + return 0; +} + +size_t +Thread::StopInfo::GetExceptionDataCount() const +{ + if (m_reason == eStopReasonException) + return m_details.exception.data_count; + return 0; +} + +void +Thread::StopInfo::SetStopReasonWithException (uint32_t exc_type, size_t exc_data_count) +{ + m_reason = eStopReasonException; + m_details.exception.type = exc_type; + m_details.exception.data_count = exc_data_count; +} + +void +Thread::StopInfo::SetStopReasonWithPlan (ThreadPlanSP &thread_plan_sp) +{ + m_reason = eStopReasonPlanComplete; + m_completed_plan_sp = thread_plan_sp; +} + +void +Thread::StopInfo::SetStopReasonToNone () +{ + Clear(); + m_reason = eStopReasonNone; +} + +lldb::addr_t +Thread::StopInfo::GetExceptionDataAtIndex (uint32_t idx) const +{ + if (m_reason == eStopReasonException && idx < m_details.exception.data_count) + return m_details.exception.data[idx]; + return 0; + +} + + +bool +Thread::StopInfo::SetExceptionDataAtIndex (uint32_t idx, lldb::addr_t data) +{ + if (m_reason == eStopReasonException && idx < m_details.exception.data_count) + { + m_details.exception.data[idx] = data; + return true; + } + return false; +} + +void +Thread::StopInfo::Dump (Stream *s) const +{ + if (m_description[0]) + s->Printf("%s", m_description); + else + { + switch (m_reason) + { + case eStopReasonInvalid: + s->PutCString("invalid"); + break; + + case eStopReasonNone: + s->PutCString("none"); + break; + + case eStopReasonTrace: + s->PutCString("trace"); + break; + + case eStopReasonBreakpoint: + { + bool no_details = true; + s->PutCString ("breakpoint "); + if (m_thread) + { + BreakpointSiteSP bp_site_sp = m_thread->GetProcess().GetBreakpointSiteList().FindByID(m_details.breakpoint.bp_site_id); + if (bp_site_sp) + { + bp_site_sp->GetDescription(s, lldb::eDescriptionLevelBrief); + no_details = false; + } + } + + if (no_details) + s->Printf ("site id: %d", m_details.breakpoint.bp_site_id); + } + break; + + case eStopReasonWatchpoint: + s->Printf("watchpoint (site id = %u)", m_details.watchpoint.watch_id); + break; + + case eStopReasonSignal: + { + s->Printf("signal: signo = %i", m_details.signal.signo); + const char * signal_name = m_thread->GetProcess().GetUnixSignals().GetSignalAsCString (m_details.signal.signo); + if (signal_name) + s->Printf(" (%s)", signal_name); + } + break; + + case eStopReasonException: + { + s->Printf("exception: type = 0x%8.8x, data_count = %zu", m_details.exception.type, m_details.exception.data_count); + uint32_t i; + for (i=0; i<m_details.exception.data_count; ++i) + { + s->Printf(", data[%u] = 0x%8.8llx", i, m_details.exception.data[i]); + } + } + break; + + case eStopReasonPlanComplete: + { + m_completed_plan_sp->GetDescription (s, lldb::eDescriptionLevelBrief); + } + break; + } + } +} + +bool +Thread::GetStopInfo (Thread::StopInfo *stop_info) +{ + stop_info->SetThread(this); + ThreadPlanSP completed_plan = GetCompletedPlan(); + if (completed_plan != NULL) + { + stop_info->Clear (); + stop_info->SetStopReasonWithPlan (completed_plan); + return true; + } + else + return GetRawStopReason (stop_info); +} + +bool +Thread::ThreadStoppedForAReason (void) +{ + Thread::StopInfo stop_info; + stop_info.SetThread(this); + if (GetRawStopReason (&stop_info)) + { + StopReason reason = stop_info.GetStopReason(); + if (reason == eStopReasonInvalid || reason == eStopReasonNone) + return false; + else + return true; + } + else + return false; +} + +StateType +Thread::GetState() const +{ + // If any other threads access this we will need a mutex for it + Mutex::Locker locker(m_state_mutex); + return m_state; +} + +void +Thread::SetState(StateType state) +{ + Mutex::Locker locker(m_state_mutex); + m_state = state; +} + +void +Thread::WillStop() +{ + ThreadPlan *current_plan = GetCurrentPlan(); + + // FIXME: I may decide to disallow threads with no plans. In which + // case this should go to an assert. + + if (!current_plan) + return; + + current_plan->WillStop(); +} + +void +Thread::SetupForResume () +{ + if (GetResumeState() != eStateSuspended) + { + + // If we're at a breakpoint push the step-over breakpoint plan. Do this before + // telling the current plan it will resume, since we might change what the current + // plan is. + + lldb::addr_t pc = GetRegisterContext()->GetPC(); + BreakpointSiteSP bp_site_sp = GetProcess().GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp && bp_site_sp->IsEnabled()) + { + // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target may not require anything + // special to step over a breakpoint. + + ThreadPlan *cur_plan = GetCurrentPlan(); + ThreadPlanStepOverBreakpoint *step_over_bp = dynamic_cast<ThreadPlanStepOverBreakpoint *> (cur_plan); + if (step_over_bp == NULL) + { + + ThreadPlanSP step_bp_plan_sp (new ThreadPlanStepOverBreakpoint (*this)); + if (step_bp_plan_sp) + { + if (GetCurrentPlan()->RunState() != eStateStepping) + { + ThreadPlanSP continue_plan_sp (new ThreadPlanContinue(*this, false, eVoteNo, eVoteNoOpinion)); + continue_plan_sp->SetPrivate (true); + QueueThreadPlan (continue_plan_sp, false); + } + step_bp_plan_sp->SetPrivate (true); + QueueThreadPlan (step_bp_plan_sp, false); + } + } + } + } +} + +bool +Thread::WillResume (StateType resume_state) +{ + // At this point clear the completed plan stack. + m_completed_plan_stack.clear(); + m_discarded_plan_stack.clear(); + + // If this thread stopped with a signal, work out what its resume state should + // be. Note if the thread resume state is already set, then don't override it, + // the user must have asked us to resume with some other signal. + + if (GetResumeSignal() == LLDB_INVALID_SIGNAL_NUMBER) + { + Thread::StopInfo stop_info; + GetRawStopReason(&stop_info); + + StopReason reason = stop_info.GetStopReason(); + if (reason == eStopReasonSignal) + { + UnixSignals &signals = GetProcess().GetUnixSignals(); + int32_t signo = stop_info.GetSignal(); + if (!signals.GetShouldSuppress(signo)) + { + SetResumeSignal(signo); + } + } + } + + // Tell all the plans that we are about to resume in case they need to clear any state. + // We distinguish between the plan on the top of the stack and the lower + // plans in case a plan needs to do any special business before it runs. + + ThreadPlan *plan_ptr = GetCurrentPlan(); + plan_ptr->WillResume(resume_state, true); + + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL) + { + plan_ptr->WillResume (resume_state, false); + } + return true; +} + +void +Thread::DidResume () +{ + SetResumeSignal (LLDB_INVALID_SIGNAL_NUMBER); +} + +bool +Thread::ShouldStop (Event* event_ptr) +{ + ThreadPlan *current_plan = GetCurrentPlan(); + bool should_stop = true; + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + StreamString s; + DumpThreadPlans(&s); + log->PutCString (s.GetData()); + } + + if (current_plan->PlanExplainsStop()) + { + while (1) + { + should_stop = current_plan->ShouldStop(event_ptr); + if (current_plan->MischiefManaged()) + { + if (should_stop) + current_plan->WillStop(); + + // If a Master Plan wants to stop, and wants to stick on the stack, we let it. + // Otherwise, see if the plan's parent wants to stop. + + if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard()) + { + PopPlan(); + break; + } + else + { + + PopPlan(); + + current_plan = GetCurrentPlan(); + if (current_plan == NULL) + { + break; + } + } + + } + else + { + break; + } + } + } + else + { + // If the current plan doesn't explain the stop, then, find one that + // does and let it handle the situation. + ThreadPlan *plan_ptr = current_plan; + while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL) + { + if (plan_ptr->PlanExplainsStop()) + { + should_stop = plan_ptr->ShouldStop (event_ptr); + break; + } + + } + } + + return should_stop; +} + +Vote +Thread::ShouldReportStop (Event* event_ptr) +{ + StateType thread_state = GetResumeState (); + if (thread_state == eStateSuspended + || thread_state == eStateInvalid) + return eVoteNoOpinion; + + if (m_completed_plan_stack.size() > 0) + { + // Don't use GetCompletedPlan here, since that suppresses private plans. + return m_completed_plan_stack.back()->ShouldReportStop (event_ptr); + } + else + return GetCurrentPlan()->ShouldReportStop (event_ptr); +} + +Vote +Thread::ShouldReportRun (Event* event_ptr) +{ + StateType thread_state = GetResumeState (); + if (thread_state == eStateSuspended + || thread_state == eStateInvalid) + return eVoteNoOpinion; + + if (m_completed_plan_stack.size() > 0) + { + // Don't use GetCompletedPlan here, since that suppresses private plans. + return m_completed_plan_stack.back()->ShouldReportRun (event_ptr); + } + else + return GetCurrentPlan()->ShouldReportRun (event_ptr); +} + +void +Thread::PushPlan (ThreadPlanSP &thread_plan_sp) +{ + if (thread_plan_sp) + { + if (thread_plan_sp->IsImmediate()) + m_immediate_plan_stack.push_back (thread_plan_sp); + else + m_plan_stack.push_back (thread_plan_sp); + + thread_plan_sp->DidPush(); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + StreamString s; + thread_plan_sp->GetDescription (&s, lldb::eDescriptionLevelFull); + log->Printf("Pushing plan: \"%s\" for thread: %d immediate: %s.", + s.GetData(), + thread_plan_sp->GetThread().GetID(), + thread_plan_sp->IsImmediate() ? "true" : "false"); + } + } +} + +void +Thread::PopPlan () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (!m_immediate_plan_stack.empty()) + { + ThreadPlanSP &plan = m_immediate_plan_stack.back(); + if (log) + { + log->Printf("Popping plan: \"%s\" for thread: %d immediate: true.", plan->GetName(), plan->GetThread().GetID()); + } + plan->WillPop(); + m_immediate_plan_stack.pop_back(); + } + else if (m_plan_stack.empty()) + return; + else + { + ThreadPlanSP &plan = m_plan_stack.back(); + if (log) + { + log->Printf("Popping plan: \"%s\" for thread: 0x%x immediate: false.", plan->GetName(), plan->GetThread().GetID()); + } + m_completed_plan_stack.push_back (plan); + plan->WillPop(); + m_plan_stack.pop_back(); + } +} + +void +Thread::DiscardPlan () +{ + if (m_plan_stack.size() > 1) + { + ThreadPlanSP &plan = m_plan_stack.back(); + m_discarded_plan_stack.push_back (plan); + plan->WillPop(); + m_plan_stack.pop_back(); + } +} + +ThreadPlan * +Thread::GetCurrentPlan () +{ + if (!m_immediate_plan_stack.empty()) + return m_immediate_plan_stack.back().get(); + else if (m_plan_stack.empty()) + return NULL; + else + return m_plan_stack.back().get(); +} + +ThreadPlanSP +Thread::GetCompletedPlan () +{ + ThreadPlanSP empty_plan_sp; + if (!m_completed_plan_stack.empty()) + { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) + { + ThreadPlanSP completed_plan_sp; + completed_plan_sp = m_completed_plan_stack[i]; + if (!completed_plan_sp->GetPrivate ()) + return completed_plan_sp; + } + } + return empty_plan_sp; +} + +bool +Thread::IsThreadPlanDone (ThreadPlan *plan) +{ + ThreadPlanSP empty_plan_sp; + if (!m_completed_plan_stack.empty()) + { + for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) + { + if (m_completed_plan_stack[i].get() == plan) + return true; + } + } + return false; +} + +bool +Thread::WasThreadPlanDiscarded (ThreadPlan *plan) +{ + ThreadPlanSP empty_plan_sp; + if (!m_discarded_plan_stack.empty()) + { + for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) + { + if (m_discarded_plan_stack[i].get() == plan) + return true; + } + } + return false; +} + +ThreadPlan * +Thread::GetPreviousPlan (ThreadPlan *current_plan) +{ + if (current_plan == NULL) + return NULL; + + int stack_size = m_completed_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + if (current_plan == m_completed_plan_stack[i].get()) + return m_completed_plan_stack[i-1].get(); + } + + if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) + { + if (m_immediate_plan_stack.size() > 0) + return m_immediate_plan_stack.back().get(); + else if (m_plan_stack.size() > 0) + return m_plan_stack.back().get(); + else + return NULL; + } + + stack_size = m_immediate_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + if (current_plan == m_immediate_plan_stack[i].get()) + return m_immediate_plan_stack[i-1].get(); + } + if (stack_size > 0 && m_immediate_plan_stack[0].get() == current_plan) + { + if (m_plan_stack.size() > 0) + return m_plan_stack.back().get(); + else + return NULL; + } + + stack_size = m_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + if (current_plan == m_plan_stack[i].get()) + return m_plan_stack[i-1].get(); + } + return NULL; +} + +void +Thread::QueueThreadPlan (ThreadPlanSP &thread_plan_sp, bool abort_other_plans) +{ + if (abort_other_plans) + DiscardThreadPlans(true); + + PushPlan (thread_plan_sp); +} + +void +Thread::DiscardThreadPlans(bool force) +{ + // FIXME: It is not always safe to just discard plans. Some, like the step over + // breakpoint trap can't be discarded in general (though you can if you plan to + // force a return from a function, for instance. + // For now I'm just not clearing immediate plans, but I need a way for plans to + // say they really need to be kept on, and then a way to override that. Humm... + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + { + log->Printf("Discarding thread plans for thread: 0x%x: force %d.", GetID(), force); + } + + if (force) + { + int stack_size = m_plan_stack.size(); + for (int i = stack_size - 1; i > 0; i--) + { + DiscardPlan(); + } + return; + } + + while (1) + { + + int master_plan_idx; + bool discard; + + // Find the first master plan, see if it wants discarding, and if yes discard up to it. + for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0; master_plan_idx--) + { + if (m_plan_stack[master_plan_idx]->IsMasterPlan()) + { + discard = m_plan_stack[master_plan_idx]->OkayToDiscard(); + break; + } + } + + if (discard) + { + // First pop all the dependent plans: + for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) + { + + // FIXME: Do we need a finalize here, or is the rule that "PrepareForStop" + // for the plan leaves it in a state that it is safe to pop the plan + // with no more notice? + DiscardPlan(); + } + + // Now discard the master plan itself. + // The bottom-most plan never gets discarded. "OkayToDiscard" for it means + // discard it's dependent plans, but not it... + if (master_plan_idx > 0) + { + DiscardPlan(); + } + } + else + { + // If the master plan doesn't want to get discarded, then we're done. + break; + } + + } + // FIXME: What should we do about the immediate plans? +} + +ThreadPlan * +Thread::QueueFundamentalPlan (bool abort_other_plans) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanBase(*this)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepSingleInstruction (bool step_over, bool abort_other_plans, bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepInstruction (*this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepRange (bool abort_other_plans, StepType type, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_other_threads) +{ + ThreadPlanSP thread_plan_sp; + if (type == eStepTypeInto) + thread_plan_sp.reset (new ThreadPlanStepInRange (*this, range, addr_context, stop_other_threads)); + else + thread_plan_sp.reset (new ThreadPlanStepOverRange (*this, range, addr_context, stop_other_threads)); + + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + + +ThreadPlan * +Thread::QueueThreadPlanForStepOverBreakpointPlan (bool abort_other_plans) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepOverBreakpoint (*this)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepOut (bool abort_other_plans, SymbolContext *addr_context, bool first_insn, + bool stop_other_threads, Vote stop_vote, Vote run_vote) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepOut (*this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepThrough (bool abort_other_plans, bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp(GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (*this, stop_other_threads)); + if (thread_plan_sp.get() == NULL) + { + thread_plan_sp.reset(new ThreadPlanStepThrough (*this, stop_other_threads)); + if (thread_plan_sp && !thread_plan_sp->ValidatePlan (NULL)) + return false; + } + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForContinue (bool abort_other_plans, bool stop_other_threads, Vote stop_vote, Vote run_vote, bool immediate) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanContinue (*this, stop_other_threads, stop_vote, run_vote, immediate)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + lldb::addr_t arg, + bool stop_other_threads, + bool discard_on_error) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, arg, stop_other_threads, discard_on_error)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + ValueList &args, + bool stop_other_threads, + bool discard_on_error) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, args, stop_other_threads, discard_on_error)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForRunToAddress (bool abort_other_plans, + Address &target_addr, + bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanRunToAddress (*this, target_addr, stop_other_threads)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); +} + +ThreadPlan * +Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanStepUntil (*this, address_list, num_addresses, stop_other_threads)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + return thread_plan_sp.get(); + +} + +uint32_t +Thread::GetIndexID () const +{ + return m_index_id; +} + +void +Thread::DumpThreadPlans (lldb_private::Stream *s) const +{ + uint32_t stack_size = m_plan_stack.size(); + s->Printf ("Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + + stack_size = m_immediate_plan_stack.size(); + s->Printf ("Immediate Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_immediate_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + + stack_size = m_completed_plan_stack.size(); + s->Printf ("Completed Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_completed_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + + stack_size = m_discarded_plan_stack.size(); + s->Printf ("Discarded Plan Stack: %d elements.\n", stack_size); + for (int i = stack_size - 1; i > 0; i--) + { + s->Printf ("Element %d: ", i); + s->IndentMore(); + m_discarded_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->IndentLess(); + s->EOL(); + } + +} + +Target * +Thread::CalculateTarget () +{ + return m_process.CalculateTarget(); +} + +Process * +Thread::CalculateProcess () +{ + return &m_process; +} + +Thread * +Thread::CalculateThread () +{ + return this; +} + +StackFrame * +Thread::CalculateStackFrame () +{ + return NULL; +} + +void +Thread::Calculate (ExecutionContext &exe_ctx) +{ + m_process.Calculate (exe_ctx); + exe_ctx.thread = this; + exe_ctx.frame = NULL; +} + +lldb::StackFrameSP +Thread::GetCurrentFrame () +{ + return GetStackFrameAtIndex (m_frames.GetCurrentFrameIndex()); +} + +uint32_t +Thread::SetCurrentFrame (lldb_private::StackFrame *frame) +{ + return m_frames.SetCurrentFrame(frame); +} + +void +Thread::SetCurrentFrameByIndex (uint32_t frame_idx) +{ + m_frames.SetCurrentFrameByIndex(frame_idx); +} + +void +Thread::DumpInfo +( + Stream &strm, + bool show_stop_reason, + bool show_name, + bool show_queue, + uint32_t frame_idx +) +{ + strm.Printf("thread #%u: tid = 0x%4.4x", GetIndexID(), GetID()); + + if (frame_idx != LLDB_INVALID_INDEX32) + { + StackFrameSP frame_sp(GetStackFrameAtIndex (frame_idx)); + if (frame_sp) + { + strm.PutCString(", "); + frame_sp->Dump (&strm, false); + } + } + + if (show_stop_reason) + { + Thread::StopInfo thread_stop_info; + if (GetStopInfo(&thread_stop_info)) + { + if (thread_stop_info.GetStopReason() != eStopReasonNone) + { + strm.PutCString(", stop reason = "); + thread_stop_info.Dump(&strm); + } + } + } + + if (show_name) + { + const char *name = GetName(); + if (name && name[0]) + strm.Printf(", name = %s", name); + } + + if (show_queue) + { + const char *queue = GetQueueName(); + if (queue && queue[0]) + strm.Printf(", queue = %s", queue); + } +} + +lldb::ThreadSP +Thread::GetSP () +{ + return m_process.GetThreadList().GetThreadSPForThreadPtr(this); +} diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp new file mode 100644 index 00000000000..6bc22712d10 --- /dev/null +++ b/lldb/source/Target/ThreadList.cpp @@ -0,0 +1,460 @@ +//===-- ThreadList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include <stdlib.h> + +#include <algorithm> + +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadList::ThreadList (Process *process) : + m_process (process), + m_stop_id (0), + m_threads(), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_current_tid (LLDB_INVALID_THREAD_ID) +{ +} + +ThreadList::ThreadList (const ThreadList &rhs) : + m_process (), + m_stop_id (), + m_threads (), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_current_tid () +{ + // Use the assignment operator since it uses the mutex + *this = rhs; +} + +const ThreadList& +ThreadList::operator = (const ThreadList& rhs) +{ + if (this != &rhs) + { + // Lock both mutexes to make sure neither side changes anyone on us + // while the assignement occurs + Mutex::Locker locker_this(m_threads_mutex); + Mutex::Locker locker_rhs(rhs.m_threads_mutex); + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads = rhs.m_threads; + m_current_tid = rhs.m_current_tid; + } + return *this; +} + + +ThreadList::~ThreadList() +{ +} + + +uint32_t +ThreadList::GetStopID () const +{ + return m_stop_id; +} + +void +ThreadList::SetStopID (uint32_t stop_id) +{ + m_stop_id = stop_id; +} + + +void +ThreadList::AddThread (ThreadSP &thread_sp) +{ + Mutex::Locker locker(m_threads_mutex); + m_threads.push_back(thread_sp); +} + +uint32_t +ThreadList::GetSize (bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + return m_threads.size(); +} + +ThreadSP +ThreadList::GetThreadAtIndex (uint32_t idx, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} + +ThreadSP +ThreadList::FindThreadByID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetID() == tid) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP +ThreadList::GetThreadSPForThreadPtr (Thread *thread_ptr) +{ + ThreadSP thread_sp; + if (thread_ptr) + { + Mutex::Locker locker(m_threads_mutex); + + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx].get() == thread_ptr) + { + thread_sp = m_threads[idx]; + break; + } + } + } + return thread_sp; +} + + + +ThreadSP +ThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetIndexID() == index_id) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +bool +ThreadList::ShouldStop (Event *event_ptr) +{ + Mutex::Locker locker(m_threads_mutex); + + // Running events should never stop, obviously... + + + bool should_stop = false; + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should stop. Don't ask + // suspended threads, however, it makes more sense for them to preserve their + // state across the times the process runs but they don't get a chance to. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if ((thread_sp->ThreadStoppedForAReason()) + && (thread_sp->GetResumeState () != eStateSuspended)) + { + should_stop |= thread_sp->ShouldStop(event_ptr); + } + } + if (should_stop) + { + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillStop (); + } + } + + return should_stop; +} + +Vote +ThreadList::ShouldReportStop (Event *event_ptr) +{ + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // For stopping, a YES vote wins over everything. A NO vote wins over NO opinion. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->ThreadStoppedForAReason() && (thread_sp->GetResumeState () != eStateSuspended)) + { + switch (thread_sp->ShouldReportStop (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + result = eVoteYes; + break; + case eVoteNo: + if (result == eVoteNoOpinion) + result = eVoteNo; + break; + } + } + } + return result; +} + +Vote +ThreadList::ShouldReportRun (Event *event_ptr) +{ + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // The rule is NO vote wins over everything, a YES vote wins over no opinion. + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState () != eStateSuspended) + + switch (thread_sp->ShouldReportRun (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + if (result == eVoteNoOpinion) + result = eVoteYes; + break; + case eVoteNo: + result = eVoteNo; + break; + } + } + return result; +} + +void +ThreadList::Clear() +{ + m_stop_id = 0; + m_threads.clear(); + m_current_tid = LLDB_INVALID_THREAD_ID; +} + +void +ThreadList::RefreshStateAfterStop () +{ + Mutex::Locker locker(m_threads_mutex); + + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->RefreshStateAfterStop (); +} + +void +ThreadList::DiscardThreadPlans () +{ + // You don't need to update the thread list here, because only threads + // that you currently know about have any thread plans. + Mutex::Locker locker(m_threads_mutex); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->DiscardThreadPlans (true); + +} + +bool +ThreadList::WillResume () +{ + // Run through the threads and perform their momentary actions. + // But we only do this for threads that are running, user suspended + // threads stay where they are. + bool success = true; + + Mutex::Locker locker(m_threads_mutex); + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // Give all the threads a last chance to set up their state before we + // negotiate who is actually going to get a chance to run... + + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->SetupForResume (); + + // Now go through the threads and see if any thread wants to run just itself. + // if so then pick one and run it. + ThreadList run_me_only_list (m_process); + + 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) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetCurrentPlan()->IsImmediate()) + { + // We first do all the immediate plans, so if we find one, set + // immediate_thread_sp and break out, and we'll pick it up first thing + // when we're negotiating which threads get to run. + immediate_thread_sp = thread_sp; + break; + } + else if (thread_sp->GetResumeState() != eStateSuspended && + thread_sp->GetCurrentPlan()->StopOthers()) + { + // You can't say "stop others" and also want yourself to be suspended. + assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); + + if (thread_sp == GetCurrentThread()) + { + run_only_current_thread = true; + run_me_only_list.Clear(); + run_me_only_list.AddThread (thread_sp); + break; + } + + run_me_only_list.AddThread (thread_sp); + } + + } + + 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->WillResume(thread_sp->GetCurrentPlan()->RunState()); + else + thread_sp->WillResume (eStateSuspended); + } + } + else if (run_me_only_list.GetSize (false) == 0) + { + // Everybody runs as they wish: + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + } + } + else + { + ThreadSP thread_to_run; + + if (run_only_current_thread) + { + thread_to_run = GetCurrentThread(); + } + else if (run_me_only_list.GetSize (false) == 1) + { + thread_to_run = run_me_only_list.GetThreadAtIndex (0); + } + else + { + int random_thread = (int) + ((run_me_only_list.GetSize (false) * (double) rand ()) / (RAND_MAX + 1.0)); + thread_to_run = run_me_only_list.GetThreadAtIndex (random_thread); + } + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp == thread_to_run) + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + else + thread_sp->WillResume (eStateSuspended); + } + } + + return success; +} + +void +ThreadList::DidResume () +{ + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + // Don't clear out threads that aren't going to get a chance to run, rather + // leave their state for the next time around. + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended) + thread_sp->DidResume (); + } +} + +ThreadSP +ThreadList::GetCurrentThread () +{ + Mutex::Locker locker(m_threads_mutex); + return FindThreadByID(m_current_tid); +} + +bool +ThreadList::SetCurrentThreadByID (lldb::tid_t tid) +{ + Mutex::Locker locker(m_threads_mutex); + if (FindThreadByID(tid).get()) + m_current_tid = tid; + else + m_current_tid = LLDB_INVALID_THREAD_ID; + + return m_current_tid != LLDB_INVALID_THREAD_ID; +} + +bool +ThreadList::SetCurrentThreadByIndexID (uint32_t index_id) +{ + Mutex::Locker locker(m_threads_mutex); + ThreadSP thread_sp (FindThreadByIndexID(index_id)); + if (thread_sp.get()) + m_current_tid = thread_sp->GetID(); + else + m_current_tid = LLDB_INVALID_THREAD_ID; + + return m_current_tid != LLDB_INVALID_THREAD_ID; +} + diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp new file mode 100644 index 00000000000..c9005c1f345 --- /dev/null +++ b/lldb/source/Target/ThreadPlan.cpp @@ -0,0 +1,185 @@ +//===-- ThreadPlan.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlan.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlan constructor +//---------------------------------------------------------------------- +ThreadPlan::ThreadPlan(const char *name, Thread &thread, Vote stop_vote, Vote run_vote) : + m_name (name), + m_thread (thread), + m_plan_complete(false), + m_plan_complete_mutex (Mutex::eMutexTypeRecursive), + m_plan_private (false), + m_stop_vote (stop_vote), + m_run_vote (run_vote), + m_okay_to_discard (false) +{ + SetID (GetNextID()); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlan::~ThreadPlan() +{ +} + +const char * +ThreadPlan::GetName () const +{ + return m_name.c_str(); +} + +Thread & +ThreadPlan::GetThread() +{ + return m_thread; +} + + +const Thread & +ThreadPlan::GetThread() const +{ + return m_thread; +} + +bool +ThreadPlan::IsPlanComplete () +{ + Mutex::Locker (m_plan_complete_mutex); + return m_plan_complete; +} + +void +ThreadPlan::SetPlanComplete () +{ + Mutex::Locker (m_plan_complete_mutex); + m_plan_complete = true; +} + +bool +ThreadPlan::MischiefManaged () +{ + Mutex::Locker (m_plan_complete_mutex); + m_plan_complete = true; + return true; +} + +Vote +ThreadPlan::ShouldReportStop (Event *event_ptr) +{ + if (m_stop_vote == eVoteNoOpinion) + { + ThreadPlan *prev_plan = GetPreviousPlan (); + if (prev_plan) + return prev_plan->ShouldReportStop (event_ptr); + } + return m_stop_vote; +} + +Vote +ThreadPlan::ShouldReportRun (Event *event_ptr) +{ + if (m_run_vote == eVoteNoOpinion) + { + ThreadPlan *prev_plan = GetPreviousPlan (); + if (prev_plan) + return prev_plan->ShouldReportRun (event_ptr); + } + return m_run_vote; +} + +bool +ThreadPlan::StopOthers () +{ + ThreadPlan *prev_plan; + prev_plan = GetPreviousPlan (); + if (prev_plan == NULL) + return false; + else + return prev_plan->StopOthers(); +} + +bool +ThreadPlan::WillResume (StateType resume_state, bool current_plan) +{ + if (current_plan) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (log) + log->Printf("About to resume the \"%s\" plan - state: %s - stop others: %d.", + m_name.c_str(), StateAsCString(resume_state), StopOthers()); + } + return true; +} + +lldb::user_id_t +ThreadPlan::GetNextID() +{ + static uint32_t g_nextPlanID = 0; + return ++g_nextPlanID; +} + +void +ThreadPlan::DidPush() +{ +} + +void +ThreadPlan::WillPop() +{ +} + +void +ThreadPlan::PushPlan (ThreadPlanSP &thread_plan_sp) +{ + m_thread.PushPlan (thread_plan_sp); +} + +ThreadPlan * +ThreadPlan::GetPreviousPlan () +{ + return m_thread.GetPreviousPlan (this); +} + +void +ThreadPlan::SetPrivate (bool input) +{ + m_plan_private = input; +} + +bool +ThreadPlan::GetPrivate (void) +{ + return m_plan_private; +} + +bool +ThreadPlan::OkayToDiscard() +{ + if (!IsMasterPlan()) + return true; + else + return m_okay_to_discard; +} + diff --git a/lldb/source/Target/ThreadPlanBase.cpp b/lldb/source/Target/ThreadPlanBase.cpp new file mode 100644 index 00000000000..283f3bef4c9 --- /dev/null +++ b/lldb/source/Target/ThreadPlanBase.cpp @@ -0,0 +1,202 @@ +//===-- ThreadPlanBase.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanBase.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +// +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/ThreadPlanContinue.h" +#include "lldb/Target/ThreadPlanStepOverBreakpoint.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanBase: This one always stops, and never has anything particular +// to do. +// FIXME: The "signal handling" policies should probably go here. +//---------------------------------------------------------------------- + +ThreadPlanBase::ThreadPlanBase (Thread &thread) : + ThreadPlan("base plan", thread, eVoteYes, eVoteNoOpinion) +{ + +} + +ThreadPlanBase::~ThreadPlanBase () +{ + +} + +void +ThreadPlanBase::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + s->Printf ("Base thread plan."); +} + +bool +ThreadPlanBase::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanBase::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanBase::ShouldStop (Event *event_ptr) +{ + m_stop_vote = eVoteYes; + m_run_vote = eVoteYes; + + Thread::StopInfo stop_info; + if (m_thread.GetStopInfo(&stop_info)) + { + StopReason reason = stop_info.GetStopReason(); + switch (reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + { + m_run_vote = eVoteNo; + m_stop_vote = eVoteNo; + return false; + } + case eStopReasonBreakpoint: + { + // The base plan checks for breakpoint hits. + + BreakpointSiteSP bp_site_sp; + //RegisterContext *reg_ctx = m_thread.GetRegisterContext(); + //lldb::addr_t pc = reg_ctx->GetPC(); + bp_site_sp = m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info.GetBreakpointSiteID()); + + if (bp_site_sp && bp_site_sp->IsEnabled()) + { + // We want to step over the breakpoint and then continue. So push these two plans. + + StoppointCallbackContext hit_context(event_ptr, &m_thread.GetProcess(), &m_thread, m_thread.GetStackFrameAtIndex(0).get()); + bool should_stop = m_thread.GetProcess().GetBreakpointSiteList().ShouldStop(&hit_context, bp_site_sp->GetID()); + + if (!should_stop) + { + // If we aren't going to stop at this breakpoint, and it is internal, don't report this stop or the subsequent + // running event. Otherwise we will post the stopped & running, but the stopped event will get marked + // with "restarted" so the UI will know to wait and expect the consequent "running". + uint32_t i; + bool is_wholly_internal = true; + + for (i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) + { + if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) + { + is_wholly_internal = false; + break; + } + } + if (is_wholly_internal) + { + m_stop_vote = eVoteNo; + m_run_vote = eVoteNo; + } + else + { + m_stop_vote = eVoteYes; + m_run_vote = eVoteYes; + } + + } + else + { + // If we are going to stop for a breakpoint, then unship the other plans + // at this point. Don't force the discard, however, so Master plans can stay + // in place if they want to. + m_thread.DiscardThreadPlans(false); + } + + return should_stop; + } + } + case eStopReasonException: + // If we crashed, discard thread plans and stop. Don't force the discard, however, + // since on rerun the target may clean up this exception and continue normally from there. + m_thread.DiscardThreadPlans(false); + return true; + case eStopReasonSignal: + { + // Check the signal handling, and if we are stopping for the signal, + // discard the plans and stop. + UnixSignals &signals = m_thread.GetProcess().GetUnixSignals(); + uint32_t signo = stop_info.GetSignal(); + if (signals.GetShouldStop(signo)) + { + m_thread.DiscardThreadPlans(false); + return true; + } + else + { + // We're not going to stop, but while we are here, let's figure out + // whether to report this. + if (signals.GetShouldNotify(signo)) + m_stop_vote = eVoteYes; + else + m_stop_vote = eVoteNo; + + return false; + } + } + default: + return true; + } + + } + + // If there's no explicit reason to stop, then we will continue. + return false; +} + +bool +ThreadPlanBase::StopOthers () +{ + return false; +} + +StateType +ThreadPlanBase::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanBase::WillStop () +{ + return true; +} + +// The base plan is never done. +bool +ThreadPlanBase::MischiefManaged () +{ + // The base plan is never done. + return false; +} + diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp new file mode 100644 index 00000000000..4b3533a3446 --- /dev/null +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -0,0 +1,250 @@ +//===-- ThreadPlanCallFunction.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanCallFunction.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanCallFunction: Plan to call a single function +//---------------------------------------------------------------------- + +ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, + Address &function, + lldb::addr_t arg, + bool stop_other_threads, + bool discard_on_error) : + ThreadPlan ("Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), + m_process(thread.GetProcess()), + m_arg_addr (arg), + m_args (NULL), + m_thread(thread), + m_stop_other_threads(stop_other_threads) +{ + + SetOkayToDiscard (discard_on_error); + + Process& process = thread.GetProcess(); + Target& target = process.GetTarget(); + const ABI *abi = process.GetABI(); + + if (!abi) + return; + + lldb::addr_t spBelowRedZone = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize(); + + SymbolContextList contexts; + SymbolContext context; + ModuleSP executableModuleSP (target.GetExecutableModule()); + + if (!executableModuleSP || + !executableModuleSP->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts)) + return; + + contexts.GetContextAtIndex(0, context); + + m_start_addr = context.symbol->GetValue(); + lldb::addr_t StartLoadAddr = m_start_addr.GetLoadAddress(&process); + + if (!thread.SaveFrameZeroState(m_register_backup)) + return; + + m_function_addr = function; + lldb::addr_t FunctionLoadAddr = m_function_addr.GetLoadAddress(&process); + + if (!abi->PrepareTrivialCall(thread, + spBelowRedZone, + FunctionLoadAddr, + StartLoadAddr, + m_arg_addr)) + return; + + m_valid = true; +} + +ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, + Address &function, + ValueList &args, + bool stop_other_threads, + bool discard_on_error) : + ThreadPlan ("Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), + m_process(thread.GetProcess()), + m_arg_addr (0), + m_args (&args), + m_thread(thread), + m_stop_other_threads(stop_other_threads) +{ + + SetOkayToDiscard (discard_on_error); + + Process& process = thread.GetProcess(); + Target& target = process.GetTarget(); + const ABI *abi = process.GetABI(); + + if(!abi) + return; + + lldb::addr_t spBelowRedZone = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize(); + + SymbolContextList contexts; + SymbolContext context; + ModuleSP executableModuleSP (target.GetExecutableModule()); + + if (!executableModuleSP || + !executableModuleSP->FindSymbolsWithNameAndType(ConstString ("start"), eSymbolTypeCode, contexts)) + return; + + contexts.GetContextAtIndex(0, context); + + m_start_addr = context.symbol->GetValue(); + lldb::addr_t StartLoadAddr = m_start_addr.GetLoadAddress(&process); + + if(!thread.SaveFrameZeroState(m_register_backup)) + return; + + m_function_addr = function; + lldb::addr_t FunctionLoadAddr = m_function_addr.GetLoadAddress(&process); + + if (!abi->PrepareNormalCall(thread, + spBelowRedZone, + FunctionLoadAddr, + StartLoadAddr, + *m_args)) + return; + + m_valid = true; +} + +ThreadPlanCallFunction::~ThreadPlanCallFunction () +{ +} + +void +ThreadPlanCallFunction::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf("Function call thread plan"); + } + else + { + if (m_args) + s->Printf("Thread plan to call 0x%llx with parsed arguments", m_function_addr.GetLoadAddress(&m_process), m_arg_addr); + else + s->Printf("Thread plan to call 0x%llx void * argument at: 0x%llx", m_function_addr.GetLoadAddress(&m_process), m_arg_addr); + } +} + +bool +ThreadPlanCallFunction::ValidatePlan (Stream *error) +{ + if (!m_valid) + return false; + + return true; +} + +bool +ThreadPlanCallFunction::PlanExplainsStop () +{ + if (!m_subplan_sp) + return false; + else + return m_subplan_sp->PlanExplainsStop(); +} + +bool +ThreadPlanCallFunction::ShouldStop (Event *event_ptr) +{ + if (PlanExplainsStop()) + { + m_thread.RestoreSaveFrameZero(m_register_backup); + m_thread.ClearStackFrames(); + SetPlanComplete(); + return true; + } + else + { + return false; + } +} + +bool +ThreadPlanCallFunction::StopOthers () +{ + return m_stop_other_threads; +} + +void +ThreadPlanCallFunction::SetStopOthers (bool new_value) +{ + if (m_subplan_sp) + { + ThreadPlanRunToAddress *address_plan = static_cast<ThreadPlanRunToAddress *>(m_subplan_sp.get()); + address_plan->SetStopOthers(new_value); + } + m_stop_other_threads = new_value; +} + +StateType +ThreadPlanCallFunction::RunState () +{ + return eStateRunning; +} + +void +ThreadPlanCallFunction::DidPush () +{ + m_subplan_sp.reset(new ThreadPlanRunToAddress(m_thread, m_start_addr, m_stop_other_threads)); + + m_thread.QueueThreadPlan(m_subplan_sp, false); + +} + +bool +ThreadPlanCallFunction::WillStop () +{ + return true; +} + +bool +ThreadPlanCallFunction::MischiefManaged () +{ + if (IsPlanComplete()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (log) + log->Printf("Completed call function plan."); + + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} diff --git a/lldb/source/Target/ThreadPlanContinue.cpp b/lldb/source/Target/ThreadPlanContinue.cpp new file mode 100644 index 00000000000..63d8a3323d0 --- /dev/null +++ b/lldb/source/Target/ThreadPlanContinue.cpp @@ -0,0 +1,120 @@ +//===-- ThreadPlanContinue.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanContinue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanContinue: Continue plan +//---------------------------------------------------------------------- + +ThreadPlanContinue::ThreadPlanContinue (Thread &thread, bool stop_others, Vote stop_vote, Vote run_vote, bool immediate) : + ThreadPlan ("Continue after previous plan", thread, stop_vote, run_vote), + m_stop_others (stop_others), + m_did_run (false), + m_immediate (immediate) +{ +} + +ThreadPlanContinue::~ThreadPlanContinue () +{ +} + +void +ThreadPlanContinue::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("continue"); + else + { + s->Printf ("Continue from the previous plan"); + } +} + +bool +ThreadPlanContinue::ValidatePlan (Stream *error) +{ + // Since we read the instruction we're stepping over from the thread, + // this plan will always work. + return true; +} + +bool +ThreadPlanContinue::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanContinue::ShouldStop (Event *event_ptr) +{ + return false; +} + +bool +ThreadPlanContinue::IsImmediate () const +{ + return m_immediate; + return false; +} + +bool +ThreadPlanContinue::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanContinue::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanContinue::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (current_plan) + { + m_did_run = true; + } + return true; +} + +bool +ThreadPlanContinue::WillStop () +{ + return true; +} + +bool +ThreadPlanContinue::MischiefManaged () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (m_did_run) + { + if (log) + log->Printf("Completed continue plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + return false; +} diff --git a/lldb/source/Target/ThreadPlanRunToAddress.cpp b/lldb/source/Target/ThreadPlanRunToAddress.cpp new file mode 100644 index 00000000000..0544ea5bcbe --- /dev/null +++ b/lldb/source/Target/ThreadPlanRunToAddress.cpp @@ -0,0 +1,176 @@ +//===-- ThreadPlanRunToAddress.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanRunToAddress.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanRunToAddress: Continue plan +//---------------------------------------------------------------------- + +ThreadPlanRunToAddress::ThreadPlanRunToAddress +( + Thread &thread, + Address &address, + bool stop_others +) : + ThreadPlan ("Run to breakpoint plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_stop_others (stop_others), + m_address (LLDB_INVALID_ADDRESS), + m_break_id (LLDB_INVALID_BREAK_ID) +{ + m_address = address.GetLoadAddress(&m_thread.GetProcess()); + SetInitialBreakpoint(); +} + +ThreadPlanRunToAddress::ThreadPlanRunToAddress +( + Thread &thread, + lldb::addr_t address, + bool stop_others +) : + ThreadPlan ("Run to breakpoint plan", thread, eVoteNoOpinion, eVoteNoOpinion), + m_stop_others (stop_others), + m_address (address), + m_break_id (LLDB_INVALID_BREAK_ID) +{ + SetInitialBreakpoint(); +} + +void +ThreadPlanRunToAddress::SetInitialBreakpoint () +{ + Breakpoint *breakpoint; + breakpoint = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_address, true).get(); + if (breakpoint != NULL) + { + m_break_id = breakpoint->GetID(); + breakpoint->SetThreadID(m_thread.GetID()); + } +} + +ThreadPlanRunToAddress::~ThreadPlanRunToAddress () +{ + if (m_break_id != LLDB_INVALID_BREAK_ID) + { + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_break_id); + } +} + +void +ThreadPlanRunToAddress::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf ("run to address: "); + s->Address (m_address, sizeof (addr_t)); + } + else + { + s->Printf ("Run to address: "); + s->Address(m_address, sizeof (addr_t)); + s->Printf (" using breakpoint: %d - ", m_break_id); + Breakpoint *breakpoint = m_thread.GetProcess().GetTarget().GetBreakpointByID (m_break_id).get(); + if (breakpoint) + breakpoint->Dump (s); + else + s->Printf ("but the breakpoint has been deleted."); + } +} + +bool +ThreadPlanRunToAddress::ValidatePlan (Stream *error) +{ + // If we couldn't set the breakpoint for some reason, then this won't + // work. + if(m_break_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +bool +ThreadPlanRunToAddress::PlanExplainsStop () +{ + return AtOurAddress(); +} + +bool +ThreadPlanRunToAddress::ShouldStop (Event *event_ptr) +{ + return false; +} + +bool +ThreadPlanRunToAddress::StopOthers () +{ + return m_stop_others; +} + +void +ThreadPlanRunToAddress::SetStopOthers (bool new_value) +{ + m_stop_others = new_value; +} + +StateType +ThreadPlanRunToAddress::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanRunToAddress::WillStop () +{ + return true; +} + +bool +ThreadPlanRunToAddress::MischiefManaged () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (AtOurAddress()) + { + // Remove the breakpoint + if (m_break_id != LLDB_INVALID_BREAK_ID) + { + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_break_id); + m_break_id = LLDB_INVALID_BREAK_ID; + } + + if (log) + log->Printf("Completed run to address plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + return false; +} + +bool +ThreadPlanRunToAddress::AtOurAddress () +{ + lldb::addr_t current_address = m_thread.GetRegisterContext()->GetPC(); + return m_address == current_address; +} diff --git a/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/lldb/source/Target/ThreadPlanShouldStopHere.cpp new file mode 100644 index 00000000000..493ab63bc33 --- /dev/null +++ b/lldb/source/Target/ThreadPlanShouldStopHere.cpp @@ -0,0 +1,53 @@ +//===-- ThreadPlanShouldStopHere.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanShouldStopHere.h" + +using namespace lldb; +using namespace lldb_private; + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// ThreadPlanShouldStopHere constructor +//---------------------------------------------------------------------- +ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner, ThreadPlanShouldStopHereCallback callback, void *baton) : + m_callback (callback), + m_baton (baton), + m_owner (owner), + m_flags (ThreadPlanShouldStopHere::eNone) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() +{ +} + +void +ThreadPlanShouldStopHere::SetShouldStopHereCallback (ThreadPlanShouldStopHereCallback callback, void *baton) +{ + m_callback = callback; + m_baton = baton; +} + +ThreadPlan * +ThreadPlanShouldStopHere::InvokeShouldStopHereCallback () +{ + if (m_callback) + return m_callback (m_owner, m_flags, m_baton); + else + return NULL; +}
\ No newline at end of file diff --git a/lldb/source/Target/ThreadPlanStepInRange.cpp b/lldb/source/Target/ThreadPlanStepInRange.cpp new file mode 100644 index 00000000000..f62fdb87c7f --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepInRange.cpp @@ -0,0 +1,154 @@ +//===-- ThreadPlanStepInRange.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepInRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" + +using namespace lldb; +using namespace lldb_private; + +uint32_t ThreadPlanStepInRange::s_default_flag_values = ThreadPlanShouldStopHere::eAvoidNoDebug; + +//---------------------------------------------------------------------- +// ThreadPlanStepInRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepInRange::ThreadPlanStepInRange +( + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others +) : + ThreadPlanStepRange ("Step Range stepping in", thread, range, addr_context, stop_others), + ThreadPlanShouldStopHere (this, ThreadPlanStepInRange::DefaultShouldStopHereCallback, NULL) +{ + SetFlagsToDefault (); +} + +ThreadPlanStepInRange::~ThreadPlanStepInRange () +{ +} + +void +ThreadPlanStepInRange::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("step in"); + else + { + s->Printf ("Stepping through range (stepping into functions): "); + m_address_range.Dump (s, &m_thread.GetProcess(), Address::DumpStyleLoadAddress); + } +} + +bool +ThreadPlanStepInRange::ShouldStop (Event *event_ptr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + m_no_more_plans = false; + + if (log) + { + StreamString s; + s.Address (m_thread.GetRegisterContext()->GetPC(), m_thread.GetProcess().GetAddressByteSize()); + log->Printf("ThreadPlanStepInRange reached %s.", s.GetData()); + } + + // If we're still in the range, keep going. + if (InRange()) + return false; + + // If we're in an older frame then we should stop. + if (FrameIsOlder()) + return true; + + // See if we are in a place we should step through (i.e. a trampoline of some sort): + // One tricky bit here is that some stubs don't push a frame, so we have to check + // both the case of a frame that is younger, or the same as this frame. + // However, if the frame is the same, and we are still in the symbol we started + // in, the we don't need to do this. This first check isn't strictly necessary, + // but it is more efficient. + + if (!FrameIsYounger() && InSymbol()) + { + SetPlanComplete(); + return true; + } + + ThreadPlan* new_plan = NULL; + + bool stop_others; + if (m_stop_others == lldb::eOnlyThisThread) + stop_others = true; + else + stop_others = false; + + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + // If not, give the "should_stop" callback a chance to push a plan to get us out of here. + // But only do that if we actually have stepped in. + if (!new_plan && FrameIsYounger()) + new_plan = InvokeShouldStopHereCallback(); + + if (new_plan == NULL) + { + m_no_more_plans = true; + SetPlanComplete(); + return true; + } + else + { + m_no_more_plans = false; + return false; + } +} + +void +ThreadPlanStepInRange::SetFlagsToDefault () +{ + GetFlags().Set(ThreadPlanStepInRange::s_default_flag_values); +} + +void +ThreadPlanStepInRange::SetDefaultFlagValue (uint32_t new_value) +{ + // TODO: Should we test this for sanity? + ThreadPlanStepInRange::s_default_flag_values = new_value; +} + +ThreadPlan * +ThreadPlanStepInRange::DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton) +{ + if (flags.IsSet(eAvoidNoDebug)) + { + StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); + + if (!frame->HasDebugInformation()) + { + // FIXME: Make sure the ThreadPlanForStepOut does the right thing with inlined functions. + return current_plan->GetThread().QueueThreadPlanForStepOut (false, NULL, true, current_plan->StopOthers(), eVoteNo, eVoteNoOpinion); + } + } + + return NULL; +} diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp new file mode 100644 index 00000000000..41c4373105b --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp @@ -0,0 +1,191 @@ +//===-- ThreadPlanStepInstruction.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Target/ThreadPlanStepInstruction.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepInstruction: Step over the current instruction +//---------------------------------------------------------------------- + +ThreadPlanStepInstruction::ThreadPlanStepInstruction +( + Thread &thread, + bool step_over, + bool stop_other_threads, + Vote stop_vote, + Vote run_vote +) : + ThreadPlan ("Step over single instruction", thread, stop_vote, run_vote), + m_instruction_addr (0), + m_step_over (step_over), + m_stack_depth(0), + m_stop_other_threads (stop_other_threads) +{ + m_instruction_addr = m_thread.GetRegisterContext()->GetPC(0); + m_stack_depth = m_thread.GetStackFrameCount(); +} + +ThreadPlanStepInstruction::~ThreadPlanStepInstruction () +{ +} + +void +ThreadPlanStepInstruction::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + if (m_step_over) + s->Printf ("instruction step over"); + else + s->Printf ("instruction step into"); + } + else + { + s->Printf ("Stepping one instruction past "); + s->Address(m_instruction_addr, sizeof (addr_t)); + if (m_step_over) + s->Printf(" stepping over calls"); + else + s->Printf(" stepping into calls"); + } +} + +bool +ThreadPlanStepInstruction::ValidatePlan (Stream *error) +{ + // Since we read the instruction we're stepping over from the thread, + // this plan will always work. + return true; +} + +bool +ThreadPlanStepInstruction::PlanExplainsStop () +{ + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + if (reason == eStopReasonTrace || reason ==eStopReasonNone) + return true; + else + return false; + } + return false; +} + +bool +ThreadPlanStepInstruction::ShouldStop (Event *event_ptr) +{ + if (m_step_over) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (m_thread.GetStackFrameCount() <= m_stack_depth) + { + if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) + { + SetPlanComplete(); + return true; + } + else + return false; + } + else + { + // We've stepped in, step back out again: + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + if (return_frame) + { + if (log) + { + StreamString s; + s.PutCString ("Stepped in to: "); + addr_t stop_addr = m_thread.GetStackFrameAtIndex(0)->GetPC().GetLoadAddress(&m_thread.GetProcess()); + s.Address (stop_addr, m_thread.GetProcess().GetAddressByteSize()); + s.PutCString (" stepping out to: "); + addr_t return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + s.Address (return_addr, m_thread.GetProcess().GetAddressByteSize()); + log->Printf("%s.", s.GetData()); + } + m_thread.QueueThreadPlanForStepOut(false, NULL, true, m_stop_other_threads, eVoteNo, eVoteNoOpinion); + return false; + } + else + { + if (log) + log->Printf("Could not find previous frame, stopping."); + SetPlanComplete(); + return true; + } + + } + + } + else + { + if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr) + { + SetPlanComplete(); + return true; + } + else + return false; + } +} + +bool +ThreadPlanStepInstruction::StopOthers () +{ + return m_stop_other_threads; +} + +StateType +ThreadPlanStepInstruction::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepInstruction::WillStop () +{ + return true; +} + +bool +ThreadPlanStepInstruction::MischiefManaged () +{ + if (IsPlanComplete()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed single instruction step plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} + diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp new file mode 100644 index 00000000000..e05a8a440a1 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOut.cpp @@ -0,0 +1,228 @@ +//===-- ThreadPlanStepOut.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepOut.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepOut: Step out of the current frame +//---------------------------------------------------------------------- + +ThreadPlanStepOut::ThreadPlanStepOut +( + Thread &thread, + SymbolContext *context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote +) : + ThreadPlan ("Step out", thread, stop_vote, run_vote), + m_step_from_context (context), + m_step_from_insn (LLDB_INVALID_ADDRESS), + m_return_addr (LLDB_INVALID_ADDRESS), + m_first_insn (first_insn), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_stop_others (stop_others) +{ + m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); + + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + if (return_frame) + { + m_return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get(); + if (return_bp != NULL) + { + return_bp->SetThreadID(m_thread.GetID()); + m_return_bp_id = return_bp->GetID(); + } + else + { + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + } + + m_stack_depth = m_thread.GetStackFrameCount(); +} + +ThreadPlanStepOut::~ThreadPlanStepOut () +{ + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) + m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id); +} + +void +ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("step out"); + else + { + s->Printf ("Stepping out from address 0x%llx to return address 0x%llx using breakpoint site %d", + (uint64_t)m_step_from_insn, + (uint64_t)m_return_addr, + m_return_bp_id); + } +} + +bool +ThreadPlanStepOut::ValidatePlan (Stream *error) +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +bool +ThreadPlanStepOut::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + { + // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened... + BreakpointSiteSP this_site = m_thread.GetProcess().GetBreakpointSiteList().FindByID (info.GetBreakpointSiteID()); + if (!this_site) + return false; + + if (this_site->IsBreakpointAtThisSite (m_return_bp_id)) + { + // If there was only one owner, then we're done. But if we also hit some + // user breakpoint on our way out, we should mark ourselves as done, but + // also not claim to explain the stop, since it is more important to report + // the user breakpoint than the step out completion. + + if (this_site->GetNumberOfOwners() == 1) + return true; + else + { + SetPlanComplete(); + return false; + } + } + else + return false; + } + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + return false; + default: + return true; + } + } + return true; +} + +bool +ThreadPlanStepOut::ShouldStop (Event *event_ptr) +{ + if (IsPlanComplete() + || m_thread.GetRegisterContext()->GetPC() == m_return_addr + || m_stack_depth > m_thread.GetStackFrameCount()) + { + SetPlanComplete(); + return true; + } + else + return false; +} + +bool +ThreadPlanStepOut::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepOut::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepOut::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + + if (current_plan) + { + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (true); + } + return true; +} + +bool +ThreadPlanStepOut::WillStop () +{ + Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (false); + return true; +} + +bool +ThreadPlanStepOut::MischiefManaged () +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + { + // If I couldn't set this breakpoint, then I'm just going to jettison myself. + return true; + } + else if (IsPlanComplete()) + { + // Did I reach my breakpoint? If so I'm done. + // + // I also check the stack depth, since if we've blown past the breakpoint for some + // reason and we're now stopping for some other reason altogether, then we're done + // with this step out operation. + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step out plan."); + m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } +} + diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp new file mode 100644 index 00000000000..2b8b06d5960 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -0,0 +1,130 @@ +//===-- ThreadPlanStepOverBreakpoint.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepOverBreakpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at the pc. +//---------------------------------------------------------------------- + +ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint (Thread &thread) : + ThreadPlan ("Step over breakpoint trap", + thread, + eVoteNo, + eVoteNoOpinion), // We need to report the run since this happens + // first in the thread plan stack when stepping + // over a breakpoint + m_breakpoint_addr (LLDB_INVALID_ADDRESS) +{ + m_breakpoint_addr = m_thread.GetRegisterContext()->GetPC(); + m_breakpoint_site_id = m_thread.GetProcess().GetBreakpointSiteList().FindIDByAddress (m_breakpoint_addr); + +} + +ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint () +{ +} + +void +ThreadPlanStepOverBreakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + s->Printf("Single stepping past breakpoint site %d at 0x%llx", m_breakpoint_site_id, (uint64_t)m_breakpoint_addr); +} + +bool +ThreadPlanStepOverBreakpoint::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepOverBreakpoint::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanStepOverBreakpoint::ShouldStop (Event *event_ptr) +{ + return false; +} + +bool +ThreadPlanStepOverBreakpoint::StopOthers () +{ + return true; +} + +StateType +ThreadPlanStepOverBreakpoint::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepOverBreakpoint::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + + if (current_plan) + { + BreakpointSiteSP bp_site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByAddress (m_breakpoint_addr)); + if (bp_site_sp && bp_site_sp->IsEnabled()) + m_thread.GetProcess().DisableBreakpoint (bp_site_sp.get()); + } + return true; +} + +bool +ThreadPlanStepOverBreakpoint::WillStop () +{ + BreakpointSiteSP bp_site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByAddress (m_breakpoint_addr)); + if (bp_site_sp) + m_thread.GetProcess().EnableBreakpoint (bp_site_sp.get()); + return true; +} + +bool +ThreadPlanStepOverBreakpoint::MischiefManaged () +{ + lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC(); + + if (pc_addr == m_breakpoint_addr) + { + // If we are still at the PC of our breakpoint, then for some reason we didn't + // get a chance to run. + return false; + } + else + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + 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().EnableBreakpoint (bp_site_sp.get()); + ThreadPlan::MischiefManaged (); + return true; + } +} + diff --git a/lldb/source/Target/ThreadPlanStepOverRange.cpp b/lldb/source/Target/ThreadPlanStepOverRange.cpp new file mode 100644 index 00000000000..ea56412a010 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -0,0 +1,119 @@ +//===-- ThreadPlanStepOverRange.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepOverRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepThrough.h" + +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// ThreadPlanStepOverRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepOverRange::ThreadPlanStepOverRange +( + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others, + bool okay_to_discard +) : + ThreadPlanStepRange ("Step range stepping over", thread, range, addr_context, stop_others) +{ + SetOkayToDiscard (okay_to_discard); +} + +ThreadPlanStepOverRange::~ThreadPlanStepOverRange () +{ +} + +void +ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf("step over"); + else + { + s->Printf ("stepping through range (stepping over functions): "); + m_address_range.Dump (s, &m_thread.GetProcess(), Address::DumpStyleLoadAddress); + } +} + +bool +ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + if (log) + { + StreamString s; + s.Address (m_thread.GetRegisterContext()->GetPC(), m_thread.GetProcess().GetAddressByteSize()); + log->Printf("ThreadPlanStepOverRange reached %s.", s.GetData()); + } + + // If we're still in the range, keep going. + if (InRange()) + return false; + + // If we're out of the range but in the same frame or in our caller's frame + // then we should stop. + // When stepping out we only step if we are forcing running one thread. + bool stop_others; + if (m_stop_others == lldb::eOnlyThisThread) + stop_others = true; + else + stop_others = false; + + ThreadPlan* new_plan = NULL; + + if (FrameIsOlder()) + return true; + else if (FrameIsYounger()) + { + new_plan = m_thread.QueueThreadPlanForStepOut (false, NULL, true, stop_others, lldb::eVoteNo, lldb::eVoteNoOpinion); + } + else if (!InSymbol()) + { + // This one is a little tricky. Sometimes we may be in a stub or something similar, + // in which case we need to get out of there. But if we are in a stub then it's + // likely going to be hard to get out from here. It is probably easiest to step into the + // stub, and then it will be straight-forward to step out. + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + } + + if (new_plan == NULL) + m_no_more_plans = true; + else + m_no_more_plans = false; + + if (new_plan == NULL) + { + // For efficiencies sake, we know we're done here so we don't have to do this + // calculation again in MischiefManaged. + SetPlanComplete(); + return true; + } + else + return false; +} diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp new file mode 100644 index 00000000000..edf80a57d52 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -0,0 +1,263 @@ +//===-- ThreadPlanStepRange.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepRange.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// ThreadPlanStepRange: Step through a stack range, either stepping over or into +// based on the value of \a type. +//---------------------------------------------------------------------- + +ThreadPlanStepRange::ThreadPlanStepRange (const char *name, Thread &thread, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_others) : + ThreadPlan (name, thread, eVoteNoOpinion, eVoteNoOpinion), + m_address_range (range), + m_addr_context (addr_context), + m_stop_others (stop_others), + m_stack_depth (0), + m_no_more_plans (false), + m_stack_id (), + m_first_run_event (true) +{ + m_stack_depth = m_thread.GetStackFrameCount(); + m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); +} + +ThreadPlanStepRange::~ThreadPlanStepRange () +{ +} + +bool +ThreadPlanStepRange::ValidatePlan (Stream *error) +{ + return true; +} + +bool +ThreadPlanStepRange::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + Thread::StopInfo info; + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + return false; + default: + return true; + } + } + return true; +} + +Vote +ThreadPlanStepRange::ShouldReportStop (Event *event_ptr) +{ + if (IsPlanComplete()) + return eVoteYes; + else + return eVoteNo; +} + +bool +ThreadPlanStepRange::InRange () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + bool ret_value = false; + + lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC(); + + ret_value = m_address_range.ContainsLoadAddress(pc_load_addr, &m_thread.GetProcess()); + + if (!ret_value) + { + // See if we've just stepped to another part of the same line number... + StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get(); + + SymbolContext new_context(frame->GetSymbolContext(eSymbolContextEverything)); + if (m_addr_context.line_entry.IsValid() && new_context.line_entry.IsValid()) + { + if ((m_addr_context.line_entry.file == new_context.line_entry.file) + && (m_addr_context.line_entry.line == new_context.line_entry.line)) + { + m_addr_context = new_context; + m_address_range = m_addr_context.line_entry.range; + ret_value = true; + if (log) + { + StreamString s; + m_address_range.Dump (&s, &m_thread.GetProcess(), Address::DumpStyleLoadAddress); + + log->Printf ("Step range plan stepped to another range of same line: %s", s.GetData()); + } + } + } + + } + + if (!ret_value && log) + log->Printf ("Step range plan out of range to 0x%llx", pc_load_addr); + + return ret_value; +} + +bool +ThreadPlanStepRange::InSymbol() +{ + lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); + Process *process = m_thread.CalculateProcess(); + + if (m_addr_context.function != NULL) + { + return m_addr_context.function->GetAddressRange().ContainsLoadAddress (cur_pc, process); + } + else if (m_addr_context.symbol != NULL) + { + return m_addr_context.symbol->GetAddressRangeRef().ContainsLoadAddress (cur_pc, process); + } + return false; +} + +// FIXME: This should also handle inlining if we aren't going to do inlining in the +// main stack. +// +// Ideally we should remember the whole stack frame list, and then compare that +// to the current list. + +bool +ThreadPlanStepRange::FrameIsYounger () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + uint32_t current_depth = m_thread.GetStackFrameCount(); + if (current_depth == m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsYounger still in start function."); + return false; + } + else if (current_depth < m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsYounger stepped out: start depth: %d current depth %d.", m_stack_depth, current_depth); + return false; + } + else + { + if (log) + log->Printf ("Step range FrameIsYounger stepped in: start depth: %d current depth %d.", m_stack_depth, current_depth); + return true; + } +} + +bool +ThreadPlanStepRange::FrameIsOlder () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + uint32_t current_depth = m_thread.GetStackFrameCount(); + if (current_depth == m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsOlder still in start function."); + return false; + } + else if (current_depth < m_stack_depth) + { + if (log) + log->Printf ("Step range FrameIsOlder stepped out: start depth: %d current depth %d.", m_stack_depth, current_depth); + return true; + } + else + { + if (log) + log->Printf ("Step range FrameIsOlder stepped in: start depth: %d current depth %d.", m_stack_depth, current_depth); + return false; + } +} + +bool +ThreadPlanStepRange::StopOthers () +{ + if (m_stop_others == lldb::eOnlyThisThread + || m_stop_others == lldb::eOnlyDuringStepping) + return true; + else + return false; +} + +bool +ThreadPlanStepRange::WillStop () +{ + return true; +} + +StateType +ThreadPlanStepRange::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepRange::MischiefManaged () +{ + bool done = true; + if (!IsPlanComplete()) + { + if (InRange()) + { + done = false; + } + else if (!FrameIsOlder()) + { + if (m_no_more_plans) + done = true; + else + done = false; + } + else + done = true; + } + + if (done) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step through range plan."); + ThreadPlan::MischiefManaged (); + return true; + } + else + { + return false; + } + +} diff --git a/lldb/source/Target/ThreadPlanStepThrough.cpp b/lldb/source/Target/ThreadPlanStepThrough.cpp new file mode 100644 index 00000000000..5e614e5aa52 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepThrough.cpp @@ -0,0 +1,137 @@ +//===-- ThreadPlanStepThrough.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepThrough.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepThrough: If the current instruction is a trampoline, step through it +// If it is the beginning of the prologue of a function, step through that as well. +// FIXME: At present only handles DYLD trampolines. +//---------------------------------------------------------------------- + +ThreadPlanStepThrough::ThreadPlanStepThrough (Thread &thread, bool stop_others) : + ThreadPlan ("Step through trampolines and prologues", thread, eVoteNoOpinion, eVoteNoOpinion), + m_start_address (0), + m_stop_others (stop_others) +{ + m_start_address = GetThread().GetRegisterContext()->GetPC(0); +} + +ThreadPlanStepThrough::~ThreadPlanStepThrough () +{ +} + +void +ThreadPlanStepThrough::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + s->Printf ("Step through"); + else + { + s->Printf ("Stepping through trampoline code from: "); + s->Address(m_start_address, sizeof (addr_t)); + } +} + +bool +ThreadPlanStepThrough::ValidatePlan (Stream *error) +{ + if (HappyToStopHere()) + return false; + else + return true; +} + +bool +ThreadPlanStepThrough::PlanExplainsStop () +{ + return true; +} + +bool +ThreadPlanStepThrough::ShouldStop (Event *event_ptr) +{ + return true; +} + +bool +ThreadPlanStepThrough::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepThrough::RunState () +{ + return eStateStepping; +} + +bool +ThreadPlanStepThrough::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume(resume_state, current_plan); + if (current_plan) + { + ThreadPlanSP sub_plan_sp(m_thread.GetProcess().GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others)); + if (sub_plan_sp != NULL) + PushPlan (sub_plan_sp); + } + return true; +} + +bool +ThreadPlanStepThrough::WillStop () +{ + return true; +} + +bool +ThreadPlanStepThrough::MischiefManaged () +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + // Stop if we're happy with the place we've landed... + + if (!HappyToStopHere()) + { + // If we are still at the PC we were trying to step over. + return false; + } + else + { + if (log) + log->Printf("Completed step through step plan."); + ThreadPlan::MischiefManaged (); + return true; + } +} + +bool +ThreadPlanStepThrough::HappyToStopHere() +{ + // This should again ask the various trampolines whether we are still at a + // trampoline point, and if so, continue through the possibly nested trampolines. + + return true; +} + diff --git a/lldb/source/Target/ThreadPlanStepUntil.cpp b/lldb/source/Target/ThreadPlanStepUntil.cpp new file mode 100644 index 00000000000..3f9cdb55047 --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepUntil.cpp @@ -0,0 +1,360 @@ +//===-- ThreadPlanStepUntil.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//m_should_stop + +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepUntil.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanStepUntil: Run until we reach a given line number or step out of the current frame +//---------------------------------------------------------------------- + +ThreadPlanStepUntil::ThreadPlanStepUntil +( + Thread &thread, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others +) : + ThreadPlan ("Step out", thread, eVoteNoOpinion, eVoteNoOpinion), + m_step_from_insn (LLDB_INVALID_ADDRESS), + m_return_addr (LLDB_INVALID_ADDRESS), + m_return_bp_id(LLDB_INVALID_BREAK_ID), + m_stepped_out(false), + m_should_stop(false), + m_explains_stop(false), + m_ran_analyze (false), + m_stop_others (stop_others) +{ + + SetOkayToDiscard(true); + // Stash away our "until" addresses: + Target &target = m_thread.GetProcess().GetTarget(); + + m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); + lldb::user_id_t thread_id = m_thread.GetID(); + + // Find the return address and set a breakpoint there: + // FIXME - can we do this more securely if we know first_insn? + + StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get(); + m_return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess()); + Breakpoint *return_bp = target.CreateBreakpoint (m_return_addr, true).get(); + if (return_bp != NULL) + { + return_bp->SetThreadID(thread_id); + m_return_bp_id = return_bp->GetID(); + } + else + { + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + + m_stack_depth = m_thread.GetStackFrameCount(); + + // Now set breakpoints on all our return addresses: + for (int i = 0; i < num_addresses; i++) + { + Breakpoint *until_bp = target.CreateBreakpoint (address_list[i], true).get(); + if (until_bp != NULL) + { + until_bp->SetThreadID(thread_id); + m_until_points[address_list[i]] = until_bp->GetID(); + } + else + { + m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID; + } + } +} + +ThreadPlanStepUntil::~ThreadPlanStepUntil () +{ + Clear(); +} + +void +ThreadPlanStepUntil::Clear() +{ + Target &target = m_thread.GetProcess().GetTarget(); + if (m_return_bp_id != LLDB_INVALID_BREAK_ID) + { + target.RemoveBreakpointByID(m_return_bp_id); + m_return_bp_id = LLDB_INVALID_BREAK_ID; + } + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + target.RemoveBreakpointByID((*pos).second); + } + m_until_points.clear(); +} + +void +ThreadPlanStepUntil::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == lldb::eDescriptionLevelBrief) + { + s->Printf ("step until"); + if (m_stepped_out) + s->Printf (" - stepped out"); + } + else + { + if (m_until_points.size() == 1) + s->Printf ("Stepping from address 0x%llx until we reach 0x%llx using breakpoint %d", + (uint64_t)m_step_from_insn, + (uint64_t) (*m_until_points.begin()).first, + (*m_until_points.begin()).second); + else + { + until_collection::iterator pos, end = m_until_points.end(); + s->Printf ("Stepping from address 0x%llx until we reach one of:", + (uint64_t)m_step_from_insn); + for (pos = m_until_points.begin(); pos != end; pos++) + { + s->Printf ("\n\t0x%llx (bp: %d)", (uint64_t) (*pos).first, (*pos).second); + } + } + s->Printf(" stepped out address is 0x%lx.", (uint64_t) m_return_addr); + } +} + +bool +ThreadPlanStepUntil::ValidatePlan (Stream *error) +{ + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + { + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + if (!LLDB_BREAK_ID_IS_VALID ((*pos).second)) + return false; + } + return true; + } +} + +void +ThreadPlanStepUntil::AnalyzeStop() +{ + if (m_ran_analyze) + return; + + Thread::StopInfo info; + m_should_stop = true; + m_explains_stop = false; + + if (m_thread.GetStopInfo (&info)) + { + StopReason reason = info.GetStopReason(); + + switch (reason) + { + case eStopReasonBreakpoint: + { + // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened... + BreakpointSiteSP this_site = m_thread.GetProcess().GetBreakpointSiteList().FindByID (info.GetBreakpointSiteID()); + if (!this_site) + { + m_explains_stop = false; + return; + } + + if (this_site->IsBreakpointAtThisSite (m_return_bp_id)) + { + // If we are at our "step out" breakpoint, and the stack depth has shrunk, then + // this is indeed our stop. + // If the stack depth has grown, then we've hit our step out breakpoint recursively. + // If we are the only breakpoint at that location, then we do explain the stop, and + // we'll just continue. + // If there was another breakpoint here, then we don't explain the stop, but we won't + // mark ourselves Completed, because maybe that breakpoint will continue, and then + // we'll finish the "until". + if (m_stack_depth > m_thread.GetStackFrameCount()) + { + m_stepped_out = true; + SetPlanComplete(); + } + else + m_should_stop = false; + + if (this_site->GetNumberOfOwners() == 1) + m_explains_stop = true; + else + m_explains_stop = false; + return; + } + else + { + // Check if we've hit one of our "until" breakpoints. + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + if (this_site->IsBreakpointAtThisSite ((*pos).second)) + { + // If we're at the right stack depth, then we're done. + if (m_stack_depth == m_thread.GetStackFrameCount()) + SetPlanComplete(); + else + m_should_stop = false; + + // Otherwise we've hit this breakpoint recursively. If we're the + // only breakpoint here, then we do explain the stop, and we'll continue. + // If not then we should let higher plans handle this stop. + if (this_site->GetNumberOfOwners() == 1) + m_explains_stop = true; + else + { + m_should_stop = true; + m_explains_stop = false; + } + return; + } + } + } + // If we get here we haven't hit any of our breakpoints, so let the higher + // plans take care of the stop. + m_explains_stop = false; + return; + } + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + m_explains_stop = false; + break; + default: + m_explains_stop = true; + break; + } + } +} + +bool +ThreadPlanStepUntil::PlanExplainsStop () +{ + // We don't explain signals or breakpoints (breakpoints that handle stepping in or + // out will be handled by a child plan. + AnalyzeStop(); + return m_explains_stop; +} + +bool +ThreadPlanStepUntil::ShouldStop (Event *event_ptr) +{ + // If we've told our self in ExplainsStop that we plan to continue, then + // do so here. Otherwise, as long as this thread has stopped for a reason, + // we will stop. + + Thread::StopInfo stop_info (&m_thread); + if (!m_thread.GetStopInfo(&stop_info) + || stop_info.GetStopReason() == eStopReasonNone) + return false; + + AnalyzeStop(); + return m_should_stop; +} + +bool +ThreadPlanStepUntil::StopOthers () +{ + return m_stop_others; +} + +StateType +ThreadPlanStepUntil::RunState () +{ + return eStateRunning; +} + +bool +ThreadPlanStepUntil::WillResume (StateType resume_state, bool current_plan) +{ + ThreadPlan::WillResume (resume_state, current_plan); + if (current_plan) + { + Target &target = m_thread.GetProcess().GetTarget(); + Breakpoint *return_bp = target.GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (true); + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + Breakpoint *until_bp = target.GetBreakpointByID((*pos).second).get(); + if (until_bp != NULL) + until_bp->SetEnabled (true); + } + } + + m_should_stop = true; + m_ran_analyze = false; + m_explains_stop = false; + return true; +} + +bool +ThreadPlanStepUntil::WillStop () +{ + Target &target = m_thread.GetProcess().GetTarget(); + Breakpoint *return_bp = target.GetBreakpointByID(m_return_bp_id).get(); + if (return_bp != NULL) + return_bp->SetEnabled (false); + + until_collection::iterator pos, end = m_until_points.end(); + for (pos = m_until_points.begin(); pos != end; pos++) + { + Breakpoint *until_bp = target.GetBreakpointByID((*pos).second).get(); + if (until_bp != NULL) + until_bp->SetEnabled (false); + } + return true; +} + +bool +ThreadPlanStepUntil::MischiefManaged () +{ + + // I'm letting "PlanExplainsStop" do all the work, and just reporting that here. + bool done = false; + if (IsPlanComplete()) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (log) + log->Printf("Completed step until plan."); + + Clear(); + done = true; + } + if (done) + ThreadPlan::MischiefManaged (); + + return done; + +} + diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp new file mode 100644 index 00000000000..e6500c5dba8 --- /dev/null +++ b/lldb/source/Target/UnixSignals.cpp @@ -0,0 +1,310 @@ +//===-- UnixSignals.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/UnixSignals.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +UnixSignals::Signal::Signal (const char *name, bool default_suppress, bool default_stop, bool default_notify) : + m_name (name), + m_conditions () +{ + m_conditions[Signal::eCondSuppress] = default_suppress; + m_conditions[Signal::eCondStop] = default_stop; + m_conditions[Signal::eCondNotify] = default_notify; +} + +//---------------------------------------------------------------------- +// UnixSignals constructor +//---------------------------------------------------------------------- +UnixSignals::UnixSignals () +{ + Reset (); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +UnixSignals::~UnixSignals () +{ +} + +void +UnixSignals::Reset () +{ + // This builds one standard set of Unix Signals. If yours aren't quite in this + // order, you can either subclass this class, and use Add & Remove to change them + // or you can subclass and build them afresh in your constructor; + m_signals.clear(); + + AddSignal(1, "SIGHUP", false, true, true ); // 1 hangup + AddSignal(2, "SIGINT", true, true, true ); // 2 interrupt + AddSignal(3, "SIGQUIT", false, true, true ); // 3 quit + AddSignal(4, "SIGILL", false, true, true ); // 4 illegal instruction (not reset when caught) + AddSignal(5, "SIGTRAP", true, true, true ); // 5 trace trap (not reset when caught) + AddSignal(6, "SIGABRT", false, true, true ); // 6 abort() + AddSignal(7, "SIGEMT", false, true, true ); // 7 pollable event ([XSR] generated, not supported) + AddSignal(8, "SIGFPE", false, true, true ); // 8 floating point exception + AddSignal(9, "SIGKILL", false, true, true ); // 9 kill (cannot be caught or ignored) + AddSignal(10, "SIGBUS", false, true, true ); // 10 bus error + AddSignal(11, "SIGSEGV", false, true, true ); // 11 segmentation violation + AddSignal(12, "SIGSYS", false, true, true ); // 12 bad argument to system call + AddSignal(13, "SIGPIPE", false, true, true ); // 13 write on a pipe with no one to read it + AddSignal(14, "SIGALRM", false, false, true ); // 14 alarm clock + AddSignal(15, "SIGTERM", false, true, true ); // 15 software termination signal from kill + AddSignal(16, "SIGURG", false, false, false); // 16 urgent condition on IO channel + AddSignal(17, "SIGSTOP", false, true, true ); // 17 sendable stop signal not from tty + AddSignal(18, "SIGTSTP", false, true, true ); // 18 stop signal from tty + AddSignal(19, "SIGCONT", false, true, true ); // 19 continue a stopped process + AddSignal(20, "SIGCHLD", false, false, true ); // 20 to parent on child stop or exit + AddSignal(21, "SIGTTIN", false, true, true ); // 21 to readers pgrp upon background tty read + AddSignal(22, "SIGTTOU", false, true, true ); // 22 like TTIN for output if (tp->t_local<OSTOP) + AddSignal(23, "SIGIO", false, false, false); // 23 input/output possible signal + AddSignal(24, "SIGXCPU", false, true, true ); // 24 exceeded CPU time limit + AddSignal(25, "SIGXFSZ", false, true, true ); // 25 exceeded file size limit + AddSignal(26, "SIGVTALRM", false, false, false); // 26 virtual time alarm + AddSignal(27, "SIGPROF", false, false, false); // 27 profiling time alarm + AddSignal(28, "SIGWINCH", false, false, false); // 28 window size changes + AddSignal(29, "SIGINFO", false, true, true ); // 29 information request + AddSignal(30, "SIGUSR1", false, true, true ); // 30 user defined signal 1 + AddSignal(31, "SIGUSR2", false, true, true ); // 31 user defined signal 2 +} +void +UnixSignals::AddSignal (int signo, const char *name, bool default_suppress, bool default_stop, bool default_notify) +{ + collection::iterator iter = m_signals.find (signo); + struct Signal new_signal (name, default_suppress, default_stop, default_notify); + + if (iter != m_signals.end()) + m_signals.erase (iter); + + m_signals.insert (iter, collection::value_type (signo, new_signal)); +} + +void +UnixSignals::RemoveSignal (int signo) +{ + collection::iterator pos = m_signals.find (signo); + if (pos != m_signals.end()) + m_signals.erase (pos); +} + +UnixSignals::Signal * +UnixSignals::GetSignalByName (const char *name, int32_t &signo) +{ + ConstString const_name (name); + + collection::iterator pos, end = m_signals.end (); + for (pos = m_signals.begin (); pos != end; pos++) + { + if (const_name == (*pos).second.m_name) + { + signo = (*pos).first; + return &((*pos).second); + } + } + return NULL; +} + + +const UnixSignals::Signal * +UnixSignals::GetSignalByName (const char *name, int32_t &signo) const +{ + ConstString const_name (name); + + collection::const_iterator pos, end = m_signals.end (); + for (pos = m_signals.begin (); pos != end; pos++) + { + if (const_name == (*pos).second.m_name) + { + signo = (*pos).first; + return &((*pos).second); + } + } + return NULL; +} + +const char * +UnixSignals::GetSignalAsCString (int signo) const +{ + collection::const_iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return NULL; + else + return (*pos).second.m_name.GetCString (); +} + + +bool +UnixSignals::SignalIsValid (int32_t signo) const +{ + return m_signals.find (signo) != m_signals.end(); +} + + +int32_t +UnixSignals::GetSignalNumberFromName (const char *name) const +{ + int32_t signo; + const Signal *signal = GetSignalByName (name, signo); + if (signal == NULL) + return LLDB_INVALID_SIGNAL_NUMBER; + else + return signo; +} + +int32_t +UnixSignals::GetFirstSignalNumber () const +{ + if (m_signals.empty()) + return LLDB_INVALID_SIGNAL_NUMBER; + + return (*m_signals.begin ()).first; +} + +int32_t +UnixSignals::GetNextSignalNumber (int32_t current_signal) const +{ + collection::const_iterator pos = m_signals.find (current_signal); + collection::const_iterator end = m_signals.end(); + if (pos == end) + return LLDB_INVALID_SIGNAL_NUMBER; + else + { + pos++; + if (pos == end) + return LLDB_INVALID_SIGNAL_NUMBER; + else + return (*pos).first; + } +} + +const char * +UnixSignals::GetSignalInfo +( + int32_t signo, + bool &should_suppress, + bool &should_stop, + bool &should_notify +) const +{ + collection::const_iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return NULL; + else + { + const Signal &signal = (*pos).second; + should_suppress = signal.m_conditions[Signal::eCondSuppress]; + should_stop = signal.m_conditions[Signal::eCondStop]; + should_notify = signal.m_conditions[Signal::eCondNotify]; + return signal.m_name.AsCString(""); + } +} + +bool +UnixSignals::GetCondition +( + int32_t signo, + UnixSignals::Signal::Condition cond_pos +) const +{ + collection::const_iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return false; + else + return (*pos).second.m_conditions[cond_pos]; +} + +bool +UnixSignals::SetCondition (int32_t signo, UnixSignals::Signal::Condition cond_pos, bool value) +{ + collection::iterator pos = m_signals.find (signo); + if (pos == m_signals.end()) + return false; + else + { + bool ret_value = (*pos).second.m_conditions[cond_pos]; + (*pos).second.m_conditions[cond_pos] = value; + return ret_value; + } +} + +bool +UnixSignals::SetCondition (const char *signal_name, UnixSignals::Signal::Condition cond_pos, bool value) +{ + int32_t signo; + Signal *signal = GetSignalByName (signal_name, signo); + if (signal == NULL) + return false; + else + { + bool ret_value = signal->m_conditions[cond_pos]; + signal->m_conditions[cond_pos] = value; + return ret_value; + } +} + +bool +UnixSignals::GetShouldSuppress (int signo) const +{ + return GetCondition (signo, Signal::eCondSuppress); +} + +bool +UnixSignals::SetShouldSuppress (int signo, bool value) +{ + return SetCondition (signo, Signal::eCondSuppress, value); +} + +bool +UnixSignals::SetShouldSuppress (const char *signal_name, bool value) +{ + return SetCondition (signal_name, Signal::eCondSuppress, value); +} + +bool +UnixSignals::GetShouldStop (int signo) const +{ + return GetCondition (signo, Signal::eCondStop); +} + +bool +UnixSignals::SetShouldStop (int signo, bool value) +{ + return SetCondition (signo, Signal::eCondStop, value); +} + +bool +UnixSignals::SetShouldStop (const char *signal_name, bool value) +{ + return SetCondition (signal_name, Signal::eCondStop, value); +} + +bool +UnixSignals::GetShouldNotify (int signo) const +{ + return GetCondition (signo, Signal::eCondNotify); +} + +bool +UnixSignals::SetShouldNotify (int signo, bool value) +{ + return SetCondition (signo, Signal::eCondNotify, value); +} + +bool +UnixSignals::SetShouldNotify (const char *signal_name, bool value) +{ + return SetCondition (signal_name, Signal::eCondNotify, value); +} |