aboutsummaryrefslogtreecommitdiff
path: root/source/Host/common/NativeProcessProtocol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host/common/NativeProcessProtocol.cpp')
-rw-r--r--source/Host/common/NativeProcessProtocol.cpp412
1 files changed, 412 insertions, 0 deletions
diff --git a/source/Host/common/NativeProcessProtocol.cpp b/source/Host/common/NativeProcessProtocol.cpp
new file mode 100644
index 000000000000..b7a77266c58c
--- /dev/null
+++ b/source/Host/common/NativeProcessProtocol.cpp
@@ -0,0 +1,412 @@
+//===-- NativeProcessProtocol.cpp -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeProcessProtocol.h"
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Target/NativeRegisterContext.h"
+
+#include "NativeThreadProtocol.h"
+#include "SoftwareBreakpoint.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// -----------------------------------------------------------------------------
+// NativeProcessProtocol Members
+// -----------------------------------------------------------------------------
+
+NativeProcessProtocol::NativeProcessProtocol (lldb::pid_t pid) :
+ m_pid (pid),
+ m_threads (),
+ m_current_thread_id (LLDB_INVALID_THREAD_ID),
+ m_threads_mutex (Mutex::eMutexTypeRecursive),
+ m_state (lldb::eStateInvalid),
+ m_state_mutex (Mutex::eMutexTypeRecursive),
+ m_exit_type (eExitTypeInvalid),
+ m_exit_status (0),
+ m_exit_description (),
+ m_delegates_mutex (Mutex::eMutexTypeRecursive),
+ m_delegates (),
+ m_breakpoint_list (),
+ m_terminal_fd (-1),
+ m_stop_id (0)
+{
+}
+
+lldb_private::Error
+NativeProcessProtocol::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info)
+{
+ // Default: not implemented.
+ return Error ("not implemented");
+}
+
+bool
+NativeProcessProtocol::GetExitStatus (ExitType *exit_type, int *status, std::string &exit_description)
+{
+ if (m_state == lldb::eStateExited)
+ {
+ *exit_type = m_exit_type;
+ *status = m_exit_status;
+ exit_description = m_exit_description;
+ return true;
+ }
+
+ *status = 0;
+ return false;
+}
+
+bool
+NativeProcessProtocol::SetExitStatus (ExitType exit_type, int status, const char *exit_description, bool bNotifyStateChange)
+{
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessProtocol::%s(%d, %d, %s, %s) called",
+ __FUNCTION__,
+ exit_type,
+ status,
+ exit_description ? exit_description : "nullptr",
+ bNotifyStateChange ? "true" : "false");
+
+ // Exit status already set
+ if (m_state == lldb::eStateExited)
+ {
+ if (log)
+ log->Printf ("NativeProcessProtocol::%s exit status already set to %d, ignoring new set to %d", __FUNCTION__, m_exit_status, status);
+ return false;
+ }
+
+ m_state = lldb::eStateExited;
+
+ m_exit_type = exit_type;
+ m_exit_status = status;
+ if (exit_description && exit_description[0])
+ m_exit_description = exit_description;
+ else
+ m_exit_description.clear();
+
+ if (bNotifyStateChange)
+ SynchronouslyNotifyProcessStateChanged (lldb::eStateExited);
+
+ return true;
+}
+
+NativeThreadProtocolSP
+NativeProcessProtocol::GetThreadAtIndex (uint32_t idx)
+{
+ Mutex::Locker locker (m_threads_mutex);
+ if (idx < m_threads.size ())
+ return m_threads[idx];
+ return NativeThreadProtocolSP ();
+}
+
+NativeThreadProtocolSP
+NativeProcessProtocol::GetThreadByID (lldb::tid_t tid)
+{
+ Mutex::Locker locker (m_threads_mutex);
+ for (auto thread_sp : m_threads)
+ {
+ if (thread_sp->GetID() == tid)
+ return thread_sp;
+ }
+ return NativeThreadProtocolSP ();
+}
+
+bool
+NativeProcessProtocol::IsAlive () const
+{
+ return m_state != eStateDetached
+ && m_state != eStateExited
+ && m_state != eStateInvalid
+ && m_state != eStateUnloaded;
+}
+
+bool
+NativeProcessProtocol::GetByteOrder (lldb::ByteOrder &byte_order) const
+{
+ ArchSpec process_arch;
+ if (!GetArchitecture (process_arch))
+ return false;
+ byte_order = process_arch.GetByteOrder ();
+ return true;
+}
+
+uint32_t
+NativeProcessProtocol::GetMaxWatchpoints () const
+{
+ // This default implementation will return the number of
+ // *hardware* breakpoints available. MacOSX and other OS
+ // implementations that support software breakpoints will want to
+ // override this correctly for their implementation.
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ // get any thread
+ NativeThreadProtocolSP thread_sp (const_cast<NativeProcessProtocol*> (this)->GetThreadAtIndex (0));
+ if (!thread_sp)
+ {
+ if (log)
+ log->Warning ("NativeProcessProtocol::%s (): failed to find a thread to grab a NativeRegisterContext!", __FUNCTION__);
+ return 0;
+ }
+
+ NativeRegisterContextSP reg_ctx_sp (thread_sp->GetRegisterContext ());
+ if (!reg_ctx_sp)
+ {
+ if (log)
+ log->Warning ("NativeProcessProtocol::%s (): failed to get a RegisterContextNativeProcess from the first thread!", __FUNCTION__);
+ return 0;
+ }
+
+ return reg_ctx_sp->NumSupportedHardwareWatchpoints ();
+}
+
+Error
+NativeProcessProtocol::SetWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware)
+{
+ // This default implementation assumes setting the watchpoint for
+ // the process will require setting the watchpoint for each of the
+ // threads. Furthermore, it will track watchpoints set for the
+ // process and will add them to each thread that is attached to
+ // via the (FIXME implement) OnThreadAttached () method.
+
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ // FIXME save the watchpoint on the set of process watchpoint vars
+ // so we can add them to a thread each time a new thread is registered.
+
+ // Update the thread list
+ UpdateThreads ();
+
+ // Keep track of the threads we successfully set the watchpoint
+ // for. If one of the thread watchpoint setting operations fails,
+ // back off and remove the watchpoint for all the threads that
+ // were successfully set so we get back to a consistent state.
+ std::vector<NativeThreadProtocolSP> watchpoint_established_threads;
+
+ // Tell each thread to set a watchpoint. In the event that
+ // hardware watchpoints are requested but the SetWatchpoint fails,
+ // try to set a software watchpoint as a fallback. It's
+ // conceivable that if there are more threads than hardware
+ // watchpoints available, some of the threads will fail to set
+ // hardware watchpoints while software ones may be available.
+ Mutex::Locker locker (m_threads_mutex);
+ for (auto thread_sp : m_threads)
+ {
+ assert (thread_sp && "thread list should not have a NULL thread!");
+ if (!thread_sp)
+ continue;
+
+ Error thread_error = thread_sp->SetWatchpoint (addr, size, watch_flags, hardware);
+ if (thread_error.Fail () && hardware)
+ {
+ // Try software watchpoints since we failed on hardware watchpoint setting
+ // and we may have just run out of hardware watchpoints.
+ thread_error = thread_sp->SetWatchpoint (addr, size, watch_flags, false);
+ if (thread_error.Success ())
+ {
+ if (log)
+ log->Warning ("hardware watchpoint requested but software watchpoint set");
+ }
+ }
+
+ if (thread_error.Success ())
+ {
+ // Remember that we set this watchpoint successfully in
+ // case we need to clear it later.
+ watchpoint_established_threads.push_back (thread_sp);
+ }
+ else
+ {
+ // Unset the watchpoint for each thread we successfully
+ // set so that we get back to a consistent state of "not
+ // set" for the watchpoint.
+ for (auto unwatch_thread_sp : watchpoint_established_threads)
+ {
+ Error remove_error = unwatch_thread_sp->RemoveWatchpoint (addr);
+ if (remove_error.Fail () && log)
+ {
+ log->Warning ("NativeProcessProtocol::%s (): RemoveWatchpoint failed for pid=%" PRIu64 ", tid=%" PRIu64 ": %s",
+ __FUNCTION__, GetID (), unwatch_thread_sp->GetID (), remove_error.AsCString ());
+ }
+ }
+
+ return thread_error;
+ }
+ }
+ return Error ();
+}
+
+Error
+NativeProcessProtocol::RemoveWatchpoint (lldb::addr_t addr)
+{
+ // FIXME remove the watchpoint on the set of process watchpoint vars
+ // so we can add them to a thread each time a new thread is registered.
+
+ // Update the thread list
+ UpdateThreads ();
+
+ Error overall_error;
+
+ Mutex::Locker locker (m_threads_mutex);
+ for (auto thread_sp : m_threads)
+ {
+ assert (thread_sp && "thread list should not have a NULL thread!");
+ if (!thread_sp)
+ continue;
+
+ const Error thread_error = thread_sp->RemoveWatchpoint (addr);
+ if (thread_error.Fail ())
+ {
+ // Keep track of the first thread error if any threads
+ // fail. We want to try to remove the watchpoint from
+ // every thread, though, even if one or more have errors.
+ if (!overall_error.Fail ())
+ overall_error = thread_error;
+ }
+ }
+ return overall_error;
+}
+
+bool
+NativeProcessProtocol::RegisterNativeDelegate (NativeDelegate &native_delegate)
+{
+ Mutex::Locker locker (m_delegates_mutex);
+ if (std::find (m_delegates.begin (), m_delegates.end (), &native_delegate) != m_delegates.end ())
+ return false;
+
+ m_delegates.push_back (&native_delegate);
+ native_delegate.InitializeDelegate (this);
+ return true;
+}
+
+bool
+NativeProcessProtocol::UnregisterNativeDelegate (NativeDelegate &native_delegate)
+{
+ Mutex::Locker locker (m_delegates_mutex);
+
+ const auto initial_size = m_delegates.size ();
+ m_delegates.erase (remove (m_delegates.begin (), m_delegates.end (), &native_delegate), m_delegates.end ());
+
+ // We removed the delegate if the count of delegates shrank after
+ // removing all copies of the given native_delegate from the vector.
+ return m_delegates.size () < initial_size;
+}
+
+void
+NativeProcessProtocol::SynchronouslyNotifyProcessStateChanged (lldb::StateType state)
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ Mutex::Locker locker (m_delegates_mutex);
+ for (auto native_delegate: m_delegates)
+ native_delegate->ProcessStateChanged (this, state);
+
+ if (log)
+ {
+ if (!m_delegates.empty ())
+ {
+ log->Printf ("NativeProcessProtocol::%s: sent state notification [%s] from process %" PRIu64,
+ __FUNCTION__, lldb_private::StateAsCString (state), GetID ());
+ }
+ else
+ {
+ log->Printf ("NativeProcessProtocol::%s: would send state notification [%s] from process %" PRIu64 ", but no delegates",
+ __FUNCTION__, lldb_private::StateAsCString (state), GetID ());
+ }
+ }
+}
+
+void
+NativeProcessProtocol::NotifyDidExec ()
+{
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("NativeProcessProtocol::%s - preparing to call delegates", __FUNCTION__);
+
+ {
+ Mutex::Locker locker (m_delegates_mutex);
+ for (auto native_delegate: m_delegates)
+ native_delegate->DidExec (this);
+ }
+}
+
+
+Error
+NativeProcessProtocol::SetSoftwareBreakpoint (lldb::addr_t addr, uint32_t size_hint)
+{
+ Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("NativeProcessProtocol::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
+
+ return m_breakpoint_list.AddRef (addr, size_hint, false,
+ [this] (lldb::addr_t addr, size_t size_hint, bool /* hardware */, NativeBreakpointSP &breakpoint_sp)->Error
+ { return SoftwareBreakpoint::CreateSoftwareBreakpoint (*this, addr, size_hint, breakpoint_sp); });
+}
+
+Error
+NativeProcessProtocol::RemoveBreakpoint (lldb::addr_t addr)
+{
+ return m_breakpoint_list.DecRef (addr);
+}
+
+Error
+NativeProcessProtocol::EnableBreakpoint (lldb::addr_t addr)
+{
+ return m_breakpoint_list.EnableBreakpoint (addr);
+}
+
+Error
+NativeProcessProtocol::DisableBreakpoint (lldb::addr_t addr)
+{
+ return m_breakpoint_list.DisableBreakpoint (addr);
+}
+
+lldb::StateType
+NativeProcessProtocol::GetState () const
+{
+ Mutex::Locker locker (m_state_mutex);
+ return m_state;
+}
+
+void
+NativeProcessProtocol::SetState (lldb::StateType state, bool notify_delegates)
+{
+ Mutex::Locker locker (m_state_mutex);
+ m_state = state;
+
+ if (StateIsStoppedState (state, false))
+ {
+ ++m_stop_id;
+
+ // Give process a chance to do any stop id bump processing, such as
+ // clearing cached data that is invalidated each time the process runs.
+ // Note if/when we support some threads running, we'll end up needing
+ // to manage this per thread and per process.
+ DoStopIDBumped (m_stop_id);
+ }
+
+ // Optionally notify delegates of the state change.
+ if (notify_delegates)
+ SynchronouslyNotifyProcessStateChanged (state);
+}
+
+uint32_t NativeProcessProtocol::GetStopID () const
+{
+ Mutex::Locker locker (m_state_mutex);
+ return m_stop_id;
+}
+
+void
+NativeProcessProtocol::DoStopIDBumped (uint32_t /* newBumpId */)
+{
+ // Default implementation does nothing.
+}