forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			1122 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1122 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- ProcessFreeBSD.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
 | |
| #include <errno.h>
 | |
| #include <pthread.h>
 | |
| #include <pthread_np.h>
 | |
| #include <stdlib.h>
 | |
| #include <sys/sysctl.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/user.h>
 | |
| #include <machine/elf.h>
 | |
| 
 | |
| // C++ Includes
 | |
| #include <mutex>
 | |
| #include <unordered_map>
 | |
| 
 | |
| // Other libraries and framework includes
 | |
| #include "lldb/Core/PluginManager.h"
 | |
| #include "lldb/Core/RegisterValue.h"
 | |
| #include "lldb/Core/State.h"
 | |
| #include "lldb/Host/Host.h"
 | |
| #include "lldb/Symbol/ObjectFile.h"
 | |
| #include "lldb/Target/DynamicLoader.h"
 | |
| #include "lldb/Target/Target.h"
 | |
| 
 | |
| #include "FreeBSDThread.h"
 | |
| #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
 | |
| #include "Plugins/Process/Utility/FreeBSDSignals.h"
 | |
| #include "Plugins/Process/Utility/InferiorCallPOSIX.h"
 | |
| #include "ProcessFreeBSD.h"
 | |
| #include "ProcessMonitor.h"
 | |
| 
 | |
| // Other libraries and framework includes
 | |
| #include "lldb/Breakpoint/BreakpointLocation.h"
 | |
| #include "lldb/Breakpoint/Watchpoint.h"
 | |
| #include "lldb/Core/Module.h"
 | |
| #include "lldb/Core/ModuleSpec.h"
 | |
| #include "lldb/Core/PluginManager.h"
 | |
| #include "lldb/Core/State.h"
 | |
| #include "lldb/Host/Host.h"
 | |
| #include "lldb/Symbol/ObjectFile.h"
 | |
| #include "lldb/Target/DynamicLoader.h"
 | |
| #include "lldb/Target/Platform.h"
 | |
| #include "lldb/Target/Target.h"
 | |
| #include "lldb/Utility/DataBufferHeap.h"
 | |
| #include "lldb/Utility/FileSpec.h"
 | |
| 
 | |
| #include "lldb/Host/posix/Fcntl.h"
 | |
| 
 | |
| #include "llvm/Support/FileSystem.h"
 | |
| #include "llvm/Support/Threading.h"
 | |
| 
 | |
| using namespace lldb;
 | |
| using namespace lldb_private;
 | |
| 
 | |
| namespace {
 | |
| UnixSignalsSP &GetFreeBSDSignals() {
 | |
|   static UnixSignalsSP s_freebsd_signals_sp(new FreeBSDSignals());
 | |
|   return s_freebsd_signals_sp;
 | |
| }
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Static functions.
 | |
| 
 | |
| lldb::ProcessSP
 | |
| ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp,
 | |
|                                lldb::ListenerSP listener_sp,
 | |
|                                const FileSpec *crash_file_path) {
 | |
|   lldb::ProcessSP process_sp;
 | |
|   if (crash_file_path == NULL)
 | |
|     process_sp.reset(
 | |
|         new ProcessFreeBSD(target_sp, listener_sp, GetFreeBSDSignals()));
 | |
|   return process_sp;
 | |
| }
 | |
| 
 | |
| void ProcessFreeBSD::Initialize() {
 | |
|   static llvm::once_flag g_once_flag;
 | |
| 
 | |
|   llvm::call_once(g_once_flag, []() {
 | |
|     PluginManager::RegisterPlugin(GetPluginNameStatic(),
 | |
|                                   GetPluginDescriptionStatic(), CreateInstance);
 | |
|   });
 | |
| }
 | |
| 
 | |
| lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() {
 | |
|   static ConstString g_name("freebsd");
 | |
|   return g_name;
 | |
| }
 | |
| 
 | |
| const char *ProcessFreeBSD::GetPluginDescriptionStatic() {
 | |
|   return "Process plugin for FreeBSD";
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // ProcessInterface protocol.
 | |
| 
 | |
| lldb_private::ConstString ProcessFreeBSD::GetPluginName() {
 | |
|   return GetPluginNameStatic();
 | |
| }
 | |
| 
 | |
| uint32_t ProcessFreeBSD::GetPluginVersion() { return 1; }
 | |
| 
 | |
| void ProcessFreeBSD::Terminate() {}
 | |
| 
 | |
| Status ProcessFreeBSD::DoDetach(bool keep_stopped) {
 | |
|   Status error;
 | |
|   if (keep_stopped) {
 | |
|     error.SetErrorString("Detaching with keep_stopped true is not currently "
 | |
|                          "supported on FreeBSD.");
 | |
|     return error;
 | |
|   }
 | |
| 
 | |
|   error = m_monitor->Detach(GetID());
 | |
| 
 | |
|   if (error.Success())
 | |
|     SetPrivateState(eStateDetached);
 | |
| 
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DoResume() {
 | |
|   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
 | |
| 
 | |
|   SetPrivateState(eStateRunning);
 | |
| 
 | |
|   std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
|   bool do_step = false;
 | |
|   bool software_single_step = !SupportHardwareSingleStepping();
 | |
| 
 | |
|   for (tid_collection::const_iterator t_pos = m_run_tids.begin(),
 | |
|                                       t_end = m_run_tids.end();
 | |
|        t_pos != t_end; ++t_pos) {
 | |
|     m_monitor->ThreadSuspend(*t_pos, false);
 | |
|   }
 | |
|   for (tid_collection::const_iterator t_pos = m_step_tids.begin(),
 | |
|                                       t_end = m_step_tids.end();
 | |
|        t_pos != t_end; ++t_pos) {
 | |
|     m_monitor->ThreadSuspend(*t_pos, false);
 | |
|     do_step = true;
 | |
|     if (software_single_step) {
 | |
|       Status error = SetupSoftwareSingleStepping(*t_pos);
 | |
|       if (error.Fail())
 | |
|         return error;
 | |
|     }
 | |
|   }
 | |
|   for (tid_collection::const_iterator t_pos = m_suspend_tids.begin(),
 | |
|                                       t_end = m_suspend_tids.end();
 | |
|        t_pos != t_end; ++t_pos) {
 | |
|     m_monitor->ThreadSuspend(*t_pos, true);
 | |
|     // XXX Cannot PT_CONTINUE properly with suspended threads.
 | |
|     do_step = true;
 | |
|   }
 | |
| 
 | |
|   if (log)
 | |
|     log->Printf("process %" PRIu64 " resuming (%s)", GetID(),
 | |
|                 do_step ? "step" : "continue");
 | |
|   if (do_step && !software_single_step)
 | |
|     m_monitor->SingleStep(GetID(), m_resume_signo);
 | |
|   else
 | |
|     m_monitor->Resume(GetID(), m_resume_signo);
 | |
| 
 | |
|   return Status();
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list,
 | |
|                                       ThreadList &new_thread_list) {
 | |
|   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
 | |
|   if (log)
 | |
|     log->Printf("ProcessFreeBSD::%s (pid = %" PRIu64 ")", __FUNCTION__,
 | |
|                 GetID());
 | |
| 
 | |
|   std::vector<lldb::pid_t> tds;
 | |
|   if (!GetMonitor().GetCurrentThreadIDs(tds)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   ThreadList old_thread_list_copy(old_thread_list);
 | |
|   for (size_t i = 0; i < tds.size(); ++i) {
 | |
|     tid_t tid = tds[i];
 | |
|     ThreadSP thread_sp(old_thread_list_copy.RemoveThreadByID(tid, false));
 | |
|     if (!thread_sp) {
 | |
|       thread_sp.reset(new FreeBSDThread(*this, tid));
 | |
|       if (log)
 | |
|         log->Printf("ProcessFreeBSD::%s new tid = %" PRIu64, __FUNCTION__, tid);
 | |
|     } else {
 | |
|       if (log)
 | |
|         log->Printf("ProcessFreeBSD::%s existing tid = %" PRIu64, __FUNCTION__,
 | |
|                     tid);
 | |
|     }
 | |
|     new_thread_list.AddThread(thread_sp);
 | |
|   }
 | |
|   for (size_t i = 0; i < old_thread_list_copy.GetSize(false); ++i) {
 | |
|     ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false));
 | |
|     if (old_thread_sp) {
 | |
|       if (log)
 | |
|         log->Printf("ProcessFreeBSD::%s remove tid", __FUNCTION__);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::WillResume() {
 | |
|   m_resume_signo = 0;
 | |
|   m_suspend_tids.clear();
 | |
|   m_run_tids.clear();
 | |
|   m_step_tids.clear();
 | |
|   return Process::WillResume();
 | |
| }
 | |
| 
 | |
| void ProcessFreeBSD::SendMessage(const ProcessMessage &message) {
 | |
|   std::lock_guard<std::recursive_mutex> guard(m_message_mutex);
 | |
| 
 | |
|   switch (message.GetKind()) {
 | |
|   case ProcessMessage::eInvalidMessage:
 | |
|     return;
 | |
| 
 | |
|   case ProcessMessage::eAttachMessage:
 | |
|     SetPrivateState(eStateStopped);
 | |
|     return;
 | |
| 
 | |
|   case ProcessMessage::eLimboMessage:
 | |
|   case ProcessMessage::eExitMessage:
 | |
|     SetExitStatus(message.GetExitStatus(), NULL);
 | |
|     break;
 | |
| 
 | |
|   case ProcessMessage::eSignalMessage:
 | |
|   case ProcessMessage::eSignalDeliveredMessage:
 | |
|   case ProcessMessage::eBreakpointMessage:
 | |
|   case ProcessMessage::eTraceMessage:
 | |
|   case ProcessMessage::eWatchpointMessage:
 | |
|   case ProcessMessage::eCrashMessage:
 | |
|     SetPrivateState(eStateStopped);
 | |
|     break;
 | |
| 
 | |
|   case ProcessMessage::eNewThreadMessage:
 | |
|     llvm_unreachable("eNewThreadMessage unexpected on FreeBSD");
 | |
|     break;
 | |
| 
 | |
|   case ProcessMessage::eExecMessage:
 | |
|     SetPrivateState(eStateStopped);
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   m_message_queue.push(message);
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Constructors and destructors.
 | |
| 
 | |
| ProcessFreeBSD::ProcessFreeBSD(lldb::TargetSP target_sp,
 | |
|                                lldb::ListenerSP listener_sp,
 | |
|                                UnixSignalsSP &unix_signals_sp)
 | |
|     : Process(target_sp, listener_sp, unix_signals_sp),
 | |
|       m_byte_order(endian::InlHostByteOrder()), m_monitor(NULL), m_module(NULL),
 | |
|       m_message_mutex(), m_exit_now(false), m_seen_initial_stop(),
 | |
|       m_resume_signo(0) {
 | |
|   // FIXME: Putting this code in the ctor and saving the byte order in a
 | |
|   // member variable is a hack to avoid const qual issues in GetByteOrder.
 | |
|   lldb::ModuleSP module = GetTarget().GetExecutableModule();
 | |
|   if (module && module->GetObjectFile())
 | |
|     m_byte_order = module->GetObjectFile()->GetByteOrder();
 | |
| }
 | |
| 
 | |
| ProcessFreeBSD::~ProcessFreeBSD() { delete m_monitor; }
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Process protocol.
 | |
| void ProcessFreeBSD::Finalize() {
 | |
|   Process::Finalize();
 | |
| 
 | |
|   if (m_monitor)
 | |
|     m_monitor->StopMonitor();
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::CanDebug(lldb::TargetSP target_sp,
 | |
|                               bool plugin_specified_by_name) {
 | |
|   // For now we are just making sure the file exists for a given module
 | |
|   ModuleSP exe_module_sp(target_sp->GetExecutableModule());
 | |
|   if (exe_module_sp.get())
 | |
|     return exe_module_sp->GetFileSpec().Exists();
 | |
|   // If there is no executable module, we return true since we might be
 | |
|   // preparing to attach.
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Status
 | |
| ProcessFreeBSD::DoAttachToProcessWithID(lldb::pid_t pid,
 | |
|                                         const ProcessAttachInfo &attach_info) {
 | |
|   Status error;
 | |
|   assert(m_monitor == NULL);
 | |
| 
 | |
|   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
 | |
|   LLDB_LOGV(log, "pid = {0}", GetID());
 | |
| 
 | |
|   m_monitor = new ProcessMonitor(this, pid, error);
 | |
| 
 | |
|   if (!error.Success())
 | |
|     return error;
 | |
| 
 | |
|   PlatformSP platform_sp(GetTarget().GetPlatform());
 | |
|   assert(platform_sp.get());
 | |
|   if (!platform_sp)
 | |
|     return error; // FIXME: Detatch?
 | |
| 
 | |
|   // Find out what we can about this process
 | |
|   ProcessInstanceInfo process_info;
 | |
|   platform_sp->GetProcessInfo(pid, process_info);
 | |
| 
 | |
|   // Resolve the executable module
 | |
|   ModuleSP exe_module_sp;
 | |
|   FileSpecList executable_search_paths(
 | |
|       Target::GetDefaultExecutableSearchPaths());
 | |
|   ModuleSpec exe_module_spec(process_info.GetExecutableFile(),
 | |
|                              GetTarget().GetArchitecture());
 | |
|   error = platform_sp->ResolveExecutable(
 | |
|       exe_module_spec, exe_module_sp,
 | |
|       executable_search_paths.GetSize() ? &executable_search_paths : NULL);
 | |
|   if (!error.Success())
 | |
|     return error;
 | |
| 
 | |
|   // Fix the target architecture if necessary
 | |
|   const ArchSpec &module_arch = exe_module_sp->GetArchitecture();
 | |
|   if (module_arch.IsValid() &&
 | |
|       !GetTarget().GetArchitecture().IsExactMatch(module_arch))
 | |
|     GetTarget().SetArchitecture(module_arch);
 | |
| 
 | |
|   // Initialize the target module list
 | |
|   GetTarget().SetExecutableModule(exe_module_sp, true);
 | |
| 
 | |
|   SetSTDIOFileDescriptor(m_monitor->GetTerminalFD());
 | |
| 
 | |
|   SetID(pid);
 | |
| 
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::WillLaunch(Module *module) {
 | |
|   Status error;
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| FileSpec
 | |
| ProcessFreeBSD::GetFileSpec(const lldb_private::FileAction *file_action,
 | |
|                             const FileSpec &default_file_spec,
 | |
|                             const FileSpec &dbg_pts_file_spec) {
 | |
|   FileSpec file_spec{};
 | |
| 
 | |
|   if (file_action && file_action->GetAction() == FileAction::eFileActionOpen) {
 | |
|     file_spec = file_action->GetFileSpec();
 | |
|     // By default the stdio paths passed in will be pseudo-terminal
 | |
|     // (/dev/pts). If so, convert to using a different default path
 | |
|     // instead to redirect I/O to the debugger console. This should
 | |
|     // also handle user overrides to /dev/null or a different file.
 | |
|     if (!file_spec || file_spec == dbg_pts_file_spec)
 | |
|       file_spec = default_file_spec;
 | |
|   }
 | |
|   return file_spec;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DoLaunch(Module *module,
 | |
|                                 ProcessLaunchInfo &launch_info) {
 | |
|   Status error;
 | |
|   assert(m_monitor == NULL);
 | |
| 
 | |
|   FileSpec working_dir = launch_info.GetWorkingDirectory();
 | |
|   namespace fs = llvm::sys::fs;
 | |
|   if (working_dir && (!working_dir.ResolvePath() ||
 | |
|                       !fs::is_directory(working_dir.GetPath()))) {
 | |
|     error.SetErrorStringWithFormat("No such file or directory: %s",
 | |
|                                    working_dir.GetCString());
 | |
|     return error;
 | |
|   }
 | |
| 
 | |
|   SetPrivateState(eStateLaunching);
 | |
| 
 | |
|   const lldb_private::FileAction *file_action;
 | |
| 
 | |
|   // Default of empty will mean to use existing open file descriptors
 | |
|   FileSpec stdin_file_spec{};
 | |
|   FileSpec stdout_file_spec{};
 | |
|   FileSpec stderr_file_spec{};
 | |
| 
 | |
|   const FileSpec dbg_pts_file_spec{launch_info.GetPTY().GetSlaveName(NULL, 0),
 | |
|                                    false};
 | |
| 
 | |
|   file_action = launch_info.GetFileActionForFD(STDIN_FILENO);
 | |
|   stdin_file_spec =
 | |
|       GetFileSpec(file_action, stdin_file_spec, dbg_pts_file_spec);
 | |
| 
 | |
|   file_action = launch_info.GetFileActionForFD(STDOUT_FILENO);
 | |
|   stdout_file_spec =
 | |
|       GetFileSpec(file_action, stdout_file_spec, dbg_pts_file_spec);
 | |
| 
 | |
|   file_action = launch_info.GetFileActionForFD(STDERR_FILENO);
 | |
|   stderr_file_spec =
 | |
|       GetFileSpec(file_action, stderr_file_spec, dbg_pts_file_spec);
 | |
| 
 | |
|   m_monitor = new ProcessMonitor(
 | |
|       this, module, launch_info.GetArguments().GetConstArgumentVector(),
 | |
|       launch_info.GetEnvironmentEntries().GetConstArgumentVector(),
 | |
|       stdin_file_spec, stdout_file_spec, stderr_file_spec, working_dir,
 | |
|       launch_info, error);
 | |
| 
 | |
|   m_module = module;
 | |
| 
 | |
|   if (!error.Success())
 | |
|     return error;
 | |
| 
 | |
|   int terminal = m_monitor->GetTerminalFD();
 | |
|   if (terminal >= 0) {
 | |
| // The reader thread will close the file descriptor when done, so we pass it a
 | |
| // copy.
 | |
| #ifdef F_DUPFD_CLOEXEC
 | |
|     int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0);
 | |
|     if (stdio == -1) {
 | |
|       error.SetErrorToErrno();
 | |
|       return error;
 | |
|     }
 | |
| #else
 | |
|     // Special case when F_DUPFD_CLOEXEC does not exist (Debian kFreeBSD)
 | |
|     int stdio = fcntl(terminal, F_DUPFD, 0);
 | |
|     if (stdio == -1) {
 | |
|       error.SetErrorToErrno();
 | |
|       return error;
 | |
|     }
 | |
|     stdio = fcntl(terminal, F_SETFD, FD_CLOEXEC);
 | |
|     if (stdio == -1) {
 | |
|       error.SetErrorToErrno();
 | |
|       return error;
 | |
|     }
 | |
| #endif
 | |
|     SetSTDIOFileDescriptor(stdio);
 | |
|   }
 | |
| 
 | |
|   SetID(m_monitor->GetPID());
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| void ProcessFreeBSD::DidLaunch() {}
 | |
| 
 | |
| addr_t ProcessFreeBSD::GetImageInfoAddress() {
 | |
|   Target *target = &GetTarget();
 | |
|   ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
 | |
|   Address addr = obj_file->GetImageInfoAddress(target);
 | |
| 
 | |
|   if (addr.IsValid())
 | |
|     return addr.GetLoadAddress(target);
 | |
|   return LLDB_INVALID_ADDRESS;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DoHalt(bool &caused_stop) {
 | |
|   Status error;
 | |
| 
 | |
|   if (IsStopped()) {
 | |
|     caused_stop = false;
 | |
|   } else if (kill(GetID(), SIGSTOP)) {
 | |
|     caused_stop = false;
 | |
|     error.SetErrorToErrno();
 | |
|   } else {
 | |
|     caused_stop = true;
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DoSignal(int signal) {
 | |
|   Status error;
 | |
| 
 | |
|   if (kill(GetID(), signal))
 | |
|     error.SetErrorToErrno();
 | |
| 
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DoDestroy() {
 | |
|   Status error;
 | |
| 
 | |
|   if (!HasExited()) {
 | |
|     assert(m_monitor);
 | |
|     m_exit_now = true;
 | |
|     if (GetID() == LLDB_INVALID_PROCESS_ID) {
 | |
|       error.SetErrorString("invalid process id");
 | |
|       return error;
 | |
|     }
 | |
|     if (!m_monitor->Kill()) {
 | |
|       error.SetErrorToErrno();
 | |
|       return error;
 | |
|     }
 | |
| 
 | |
|     SetPrivateState(eStateExited);
 | |
|   }
 | |
| 
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| void ProcessFreeBSD::DoDidExec() {
 | |
|   Target *target = &GetTarget();
 | |
|   if (target) {
 | |
|     PlatformSP platform_sp(target->GetPlatform());
 | |
|     assert(platform_sp.get());
 | |
|     if (platform_sp) {
 | |
|       ProcessInstanceInfo process_info;
 | |
|       platform_sp->GetProcessInfo(GetID(), process_info);
 | |
|       ModuleSP exe_module_sp;
 | |
|       ModuleSpec exe_module_spec(process_info.GetExecutableFile(),
 | |
|                                  target->GetArchitecture());
 | |
|       FileSpecList executable_search_paths(
 | |
|           Target::GetDefaultExecutableSearchPaths());
 | |
|       Status error = platform_sp->ResolveExecutable(
 | |
|           exe_module_spec, exe_module_sp,
 | |
|           executable_search_paths.GetSize() ? &executable_search_paths : NULL);
 | |
|       if (!error.Success())
 | |
|         return;
 | |
|       target->SetExecutableModule(exe_module_sp, true);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid) {
 | |
|   bool added_to_set = false;
 | |
|   ThreadStopSet::iterator it = m_seen_initial_stop.find(stop_tid);
 | |
|   if (it == m_seen_initial_stop.end()) {
 | |
|     m_seen_initial_stop.insert(stop_tid);
 | |
|     added_to_set = true;
 | |
|   }
 | |
|   return added_to_set;
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::WaitingForInitialStop(lldb::tid_t stop_tid) {
 | |
|   return (m_seen_initial_stop.find(stop_tid) == m_seen_initial_stop.end());
 | |
| }
 | |
| 
 | |
| FreeBSDThread *
 | |
| ProcessFreeBSD::CreateNewFreeBSDThread(lldb_private::Process &process,
 | |
|                                        lldb::tid_t tid) {
 | |
|   return new FreeBSDThread(process, tid);
 | |
| }
 | |
| 
 | |
| void ProcessFreeBSD::RefreshStateAfterStop() {
 | |
|   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
 | |
|   LLDB_LOGV(log, "message_queue size = {0}", m_message_queue.size());
 | |
| 
 | |
|   std::lock_guard<std::recursive_mutex> guard(m_message_mutex);
 | |
| 
 | |
|   // This method used to only handle one message.  Changing it to loop allows
 | |
|   // it to handle the case where we hit a breakpoint while handling a different
 | |
|   // breakpoint.
 | |
|   while (!m_message_queue.empty()) {
 | |
|     ProcessMessage &message = m_message_queue.front();
 | |
| 
 | |
|     // Resolve the thread this message corresponds to and pass it along.
 | |
|     lldb::tid_t tid = message.GetTID();
 | |
|     LLDB_LOGV(log, " message_queue size = {0}, pid = {1}",
 | |
|               m_message_queue.size(), tid);
 | |
| 
 | |
|     m_thread_list.RefreshStateAfterStop();
 | |
| 
 | |
|     FreeBSDThread *thread = static_cast<FreeBSDThread *>(
 | |
|         GetThreadList().FindThreadByID(tid, false).get());
 | |
|     if (thread)
 | |
|       thread->Notify(message);
 | |
| 
 | |
|     if (message.GetKind() == ProcessMessage::eExitMessage) {
 | |
|       // FIXME: We should tell the user about this, but the limbo message is
 | |
|       // probably better for that.
 | |
|       LLDB_LOG(log, "removing thread, tid = {0}", tid);
 | |
|       std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
| 
 | |
|       ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false);
 | |
|       thread_sp.reset();
 | |
|       m_seen_initial_stop.erase(tid);
 | |
|     }
 | |
| 
 | |
|     m_message_queue.pop();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::IsAlive() {
 | |
|   StateType state = GetPrivateState();
 | |
|   return state != eStateDetached && state != eStateExited &&
 | |
|          state != eStateInvalid && state != eStateUnloaded;
 | |
| }
 | |
| 
 | |
| size_t ProcessFreeBSD::DoReadMemory(addr_t vm_addr, void *buf, size_t size,
 | |
|                                     Status &error) {
 | |
|   assert(m_monitor);
 | |
|   return m_monitor->ReadMemory(vm_addr, buf, size, error);
 | |
| }
 | |
| 
 | |
| size_t ProcessFreeBSD::DoWriteMemory(addr_t vm_addr, const void *buf,
 | |
|                                      size_t size, Status &error) {
 | |
|   assert(m_monitor);
 | |
|   return m_monitor->WriteMemory(vm_addr, buf, size, error);
 | |
| }
 | |
| 
 | |
| addr_t ProcessFreeBSD::DoAllocateMemory(size_t size, uint32_t permissions,
 | |
|                                         Status &error) {
 | |
|   addr_t allocated_addr = LLDB_INVALID_ADDRESS;
 | |
| 
 | |
|   unsigned prot = 0;
 | |
|   if (permissions & lldb::ePermissionsReadable)
 | |
|     prot |= eMmapProtRead;
 | |
|   if (permissions & lldb::ePermissionsWritable)
 | |
|     prot |= eMmapProtWrite;
 | |
|   if (permissions & lldb::ePermissionsExecutable)
 | |
|     prot |= eMmapProtExec;
 | |
| 
 | |
|   if (InferiorCallMmap(this, allocated_addr, 0, size, prot,
 | |
|                        eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) {
 | |
|     m_addr_to_mmap_size[allocated_addr] = size;
 | |
|     error.Clear();
 | |
|   } else {
 | |
|     allocated_addr = LLDB_INVALID_ADDRESS;
 | |
|     error.SetErrorStringWithFormat(
 | |
|         "unable to allocate %zu bytes of memory with permissions %s", size,
 | |
|         GetPermissionsAsCString(permissions));
 | |
|   }
 | |
| 
 | |
|   return allocated_addr;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DoDeallocateMemory(lldb::addr_t addr) {
 | |
|   Status error;
 | |
|   MMapMap::iterator pos = m_addr_to_mmap_size.find(addr);
 | |
|   if (pos != m_addr_to_mmap_size.end() &&
 | |
|       InferiorCallMunmap(this, addr, pos->second))
 | |
|     m_addr_to_mmap_size.erase(pos);
 | |
|   else
 | |
|     error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64,
 | |
|                                    addr);
 | |
| 
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| size_t
 | |
| ProcessFreeBSD::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) {
 | |
|   static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xD4};
 | |
|   static const uint8_t g_i386_opcode[] = {0xCC};
 | |
| 
 | |
|   ArchSpec arch = GetTarget().GetArchitecture();
 | |
|   const uint8_t *opcode = NULL;
 | |
|   size_t opcode_size = 0;
 | |
| 
 | |
|   switch (arch.GetMachine()) {
 | |
|   default:
 | |
|     assert(false && "CPU type not supported!");
 | |
|     break;
 | |
| 
 | |
|   case llvm::Triple::arm: {
 | |
|     // The ARM reference recommends the use of 0xe7fddefe and 0xdefe
 | |
|     // but the linux kernel does otherwise.
 | |
|     static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7};
 | |
|     static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde};
 | |
| 
 | |
|     lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
 | |
|     AddressClass addr_class = eAddressClassUnknown;
 | |
| 
 | |
|     if (bp_loc_sp)
 | |
|       addr_class = bp_loc_sp->GetAddress().GetAddressClass();
 | |
| 
 | |
|     if (addr_class == eAddressClassCodeAlternateISA ||
 | |
|         (addr_class == eAddressClassUnknown &&
 | |
|          bp_loc_sp->GetAddress().GetOffset() & 1)) {
 | |
|       opcode = g_thumb_breakpoint_opcode;
 | |
|       opcode_size = sizeof(g_thumb_breakpoint_opcode);
 | |
|     } else {
 | |
|       opcode = g_arm_breakpoint_opcode;
 | |
|       opcode_size = sizeof(g_arm_breakpoint_opcode);
 | |
|     }
 | |
|   } break;
 | |
|   case llvm::Triple::aarch64:
 | |
|     opcode = g_aarch64_opcode;
 | |
|     opcode_size = sizeof(g_aarch64_opcode);
 | |
|     break;
 | |
| 
 | |
|   case llvm::Triple::x86:
 | |
|   case llvm::Triple::x86_64:
 | |
|     opcode = g_i386_opcode;
 | |
|     opcode_size = sizeof(g_i386_opcode);
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   bp_site->SetTrapOpcode(opcode, opcode_size);
 | |
|   return opcode_size;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::EnableBreakpointSite(BreakpointSite *bp_site) {
 | |
|   return EnableSoftwareBreakpoint(bp_site);
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DisableBreakpointSite(BreakpointSite *bp_site) {
 | |
|   return DisableSoftwareBreakpoint(bp_site);
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::EnableWatchpoint(Watchpoint *wp, bool notify) {
 | |
|   Status error;
 | |
|   if (wp) {
 | |
|     user_id_t watchID = wp->GetID();
 | |
|     addr_t addr = wp->GetLoadAddress();
 | |
|     Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
 | |
|     if (log)
 | |
|       log->Printf("ProcessFreeBSD::EnableWatchpoint(watchID = %" PRIu64 ")",
 | |
|                   watchID);
 | |
|     if (wp->IsEnabled()) {
 | |
|       if (log)
 | |
|         log->Printf("ProcessFreeBSD::EnableWatchpoint(watchID = %" PRIu64
 | |
|                     ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.",
 | |
|                     watchID, (uint64_t)addr);
 | |
|       return error;
 | |
|     }
 | |
| 
 | |
|     // Try to find a vacant watchpoint slot in the inferiors' main thread
 | |
|     uint32_t wp_hw_index = LLDB_INVALID_INDEX32;
 | |
|     std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
|     FreeBSDThread *thread = static_cast<FreeBSDThread *>(
 | |
|         m_thread_list.GetThreadAtIndex(0, false).get());
 | |
| 
 | |
|     if (thread)
 | |
|       wp_hw_index = thread->FindVacantWatchpointIndex();
 | |
| 
 | |
|     if (wp_hw_index == LLDB_INVALID_INDEX32) {
 | |
|       error.SetErrorString("Setting hardware watchpoint failed.");
 | |
|     } else {
 | |
|       wp->SetHardwareIndex(wp_hw_index);
 | |
|       bool wp_enabled = true;
 | |
|       uint32_t thread_count = m_thread_list.GetSize(false);
 | |
|       for (uint32_t i = 0; i < thread_count; ++i) {
 | |
|         thread = static_cast<FreeBSDThread *>(
 | |
|             m_thread_list.GetThreadAtIndex(i, false).get());
 | |
|         if (thread)
 | |
|           wp_enabled &= thread->EnableHardwareWatchpoint(wp);
 | |
|         else
 | |
|           wp_enabled = false;
 | |
|       }
 | |
|       if (wp_enabled) {
 | |
|         wp->SetEnabled(true, notify);
 | |
|         return error;
 | |
|       } else {
 | |
|         // Watchpoint enabling failed on at least one
 | |
|         // of the threads so roll back all of them
 | |
|         DisableWatchpoint(wp, false);
 | |
|         error.SetErrorString("Setting hardware watchpoint failed");
 | |
|       }
 | |
|     }
 | |
|   } else
 | |
|     error.SetErrorString("Watchpoint argument was NULL.");
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::DisableWatchpoint(Watchpoint *wp, bool notify) {
 | |
|   Status error;
 | |
|   if (wp) {
 | |
|     user_id_t watchID = wp->GetID();
 | |
|     addr_t addr = wp->GetLoadAddress();
 | |
|     Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS));
 | |
|     if (log)
 | |
|       log->Printf("ProcessFreeBSD::DisableWatchpoint(watchID = %" PRIu64 ")",
 | |
|                   watchID);
 | |
|     if (!wp->IsEnabled()) {
 | |
|       if (log)
 | |
|         log->Printf("ProcessFreeBSD::DisableWatchpoint(watchID = %" PRIu64
 | |
|                     ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.",
 | |
|                     watchID, (uint64_t)addr);
 | |
|       // This is needed (for now) to keep watchpoints disabled correctly
 | |
|       wp->SetEnabled(false, notify);
 | |
|       return error;
 | |
|     }
 | |
| 
 | |
|     if (wp->IsHardware()) {
 | |
|       bool wp_disabled = true;
 | |
|       std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
|       uint32_t thread_count = m_thread_list.GetSize(false);
 | |
|       for (uint32_t i = 0; i < thread_count; ++i) {
 | |
|         FreeBSDThread *thread = static_cast<FreeBSDThread *>(
 | |
|             m_thread_list.GetThreadAtIndex(i, false).get());
 | |
|         if (thread)
 | |
|           wp_disabled &= thread->DisableHardwareWatchpoint(wp);
 | |
|         else
 | |
|           wp_disabled = false;
 | |
|       }
 | |
|       if (wp_disabled) {
 | |
|         wp->SetHardwareIndex(LLDB_INVALID_INDEX32);
 | |
|         wp->SetEnabled(false, notify);
 | |
|         return error;
 | |
|       } else
 | |
|         error.SetErrorString("Disabling hardware watchpoint failed");
 | |
|     }
 | |
|   } else
 | |
|     error.SetErrorString("Watchpoint argument was NULL.");
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num) {
 | |
|   Status error;
 | |
|   std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
|   FreeBSDThread *thread = static_cast<FreeBSDThread *>(
 | |
|       m_thread_list.GetThreadAtIndex(0, false).get());
 | |
|   if (thread)
 | |
|     num = thread->NumSupportedHardwareWatchpoints();
 | |
|   else
 | |
|     error.SetErrorString("Process does not exist.");
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num, bool &after) {
 | |
|   Status error = GetWatchpointSupportInfo(num);
 | |
|   // Watchpoints trigger and halt the inferior after
 | |
|   // the corresponding instruction has been executed.
 | |
|   after = true;
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| uint32_t ProcessFreeBSD::UpdateThreadListIfNeeded() {
 | |
|   std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
|   // Do not allow recursive updates.
 | |
|   return m_thread_list.GetSize(false);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| bool
 | |
| ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list)
 | |
| {
 | |
|     Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD));
 | |
|     if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
 | |
|         log->Printf ("ProcessFreeBSD::%s() (pid = %" PRIi64 ")", __FUNCTION__, GetID());
 | |
| 
 | |
|     bool has_updated = false;
 | |
|     // Update the process thread list with this new thread.
 | |
|     // FIXME: We should be using tid, not pid.
 | |
|     assert(m_monitor);
 | |
|     ThreadSP thread_sp (old_thread_list.FindThreadByID (GetID(), false));
 | |
|     if (!thread_sp) {
 | |
|         thread_sp.reset(CreateNewFreeBSDThread(*this, GetID()));
 | |
|         has_updated = true;
 | |
|     }
 | |
| 
 | |
|     if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
 | |
|         log->Printf ("ProcessFreeBSD::%s() updated pid = %" PRIi64, __FUNCTION__, GetID());
 | |
|     new_thread_list.AddThread(thread_sp);
 | |
| 
 | |
|     return has_updated; // the list has been updated
 | |
| }
 | |
| #endif
 | |
| 
 | |
| ByteOrder ProcessFreeBSD::GetByteOrder() const {
 | |
|   // FIXME: We should be able to extract this value directly.  See comment in
 | |
|   // ProcessFreeBSD().
 | |
|   return m_byte_order;
 | |
| }
 | |
| 
 | |
| size_t ProcessFreeBSD::PutSTDIN(const char *buf, size_t len, Status &error) {
 | |
|   ssize_t status;
 | |
|   if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0) {
 | |
|     error.SetErrorToErrno();
 | |
|     return 0;
 | |
|   }
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Utility functions.
 | |
| 
 | |
| bool ProcessFreeBSD::HasExited() {
 | |
|   switch (GetPrivateState()) {
 | |
|   default:
 | |
|     break;
 | |
| 
 | |
|   case eStateDetached:
 | |
|   case eStateExited:
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::IsStopped() {
 | |
|   switch (GetPrivateState()) {
 | |
|   default:
 | |
|     break;
 | |
| 
 | |
|   case eStateStopped:
 | |
|   case eStateCrashed:
 | |
|   case eStateSuspended:
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::IsAThreadRunning() {
 | |
|   bool is_running = false;
 | |
|   std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
 | |
|   uint32_t thread_count = m_thread_list.GetSize(false);
 | |
|   for (uint32_t i = 0; i < thread_count; ++i) {
 | |
|     FreeBSDThread *thread = static_cast<FreeBSDThread *>(
 | |
|         m_thread_list.GetThreadAtIndex(i, false).get());
 | |
|     StateType thread_state = thread->GetState();
 | |
|     if (thread_state == eStateRunning || thread_state == eStateStepping) {
 | |
|       is_running = true;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   return is_running;
 | |
| }
 | |
| 
 | |
| const DataBufferSP ProcessFreeBSD::GetAuxvData() {
 | |
|   // If we're the local platform, we can ask the host for auxv data.
 | |
|   PlatformSP platform_sp = GetTarget().GetPlatform();
 | |
|   assert(platform_sp && platform_sp->IsHost());
 | |
| 
 | |
|   int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, (int)m_process->GetID()};
 | |
|   size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo);
 | |
|   DataBufferSP buf_sp(new DataBufferHeap(auxv_size, 0));
 | |
| 
 | |
|   if (::sysctl(mib, 4, buf_sp->GetBytes(), &auxv_size, NULL, 0) != 0) {
 | |
|     perror("sysctl failed on auxv");
 | |
|     buf_sp.reset();
 | |
|   }
 | |
| 
 | |
|   return buf_sp;
 | |
| }
 | |
| 
 | |
| struct EmulatorBaton {
 | |
|   ProcessFreeBSD *m_process;
 | |
|   RegisterContext *m_reg_context;
 | |
| 
 | |
|   // eRegisterKindDWARF -> RegisterValue
 | |
|   std::unordered_map<uint32_t, RegisterValue> m_register_values;
 | |
| 
 | |
|   EmulatorBaton(ProcessFreeBSD *process, RegisterContext *reg_context)
 | |
|       : m_process(process), m_reg_context(reg_context) {}
 | |
| };
 | |
| 
 | |
| static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
 | |
|                                  const EmulateInstruction::Context &context,
 | |
|                                  lldb::addr_t addr, void *dst, size_t length) {
 | |
|   EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 | |
| 
 | |
|   Status error;
 | |
|   size_t bytes_read =
 | |
|       emulator_baton->m_process->DoReadMemory(addr, dst, length, error);
 | |
|   if (!error.Success())
 | |
|     bytes_read = 0;
 | |
|   return bytes_read;
 | |
| }
 | |
| 
 | |
| static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
 | |
|                                  const RegisterInfo *reg_info,
 | |
|                                  RegisterValue ®_value) {
 | |
|   EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 | |
| 
 | |
|   auto it = emulator_baton->m_register_values.find(
 | |
|       reg_info->kinds[eRegisterKindDWARF]);
 | |
|   if (it != emulator_baton->m_register_values.end()) {
 | |
|     reg_value = it->second;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // The emulator only fills in the dwarf register numbers (and in some cases
 | |
|   // the generic register numbers). Get the full register info from the
 | |
|   // register context based on the dwarf register numbers.
 | |
|   const RegisterInfo *full_reg_info =
 | |
|       emulator_baton->m_reg_context->GetRegisterInfo(
 | |
|           eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
 | |
| 
 | |
|   bool error =
 | |
|       emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
 | |
|                                   const EmulateInstruction::Context &context,
 | |
|                                   const RegisterInfo *reg_info,
 | |
|                                   const RegisterValue ®_value) {
 | |
|   EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 | |
|   emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
 | |
|       reg_value;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
 | |
|                                   const EmulateInstruction::Context &context,
 | |
|                                   lldb::addr_t addr, const void *dst,
 | |
|                                   size_t length) {
 | |
|   return length;
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::SingleStepBreakpointHit(
 | |
|     void *baton, lldb_private::StoppointCallbackContext *context,
 | |
|     lldb::user_id_t break_id, lldb::user_id_t break_loc_id) {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::SetSoftwareSingleStepBreakpoint(lldb::tid_t tid,
 | |
|                                                        lldb::addr_t addr) {
 | |
|   Status error;
 | |
| 
 | |
|   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
 | |
|   if (log) {
 | |
|     log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
 | |
|     log->Printf("SoftwareBreakpoint::%s addr = 0x%" PRIx64, __FUNCTION__, addr);
 | |
|   }
 | |
| 
 | |
|   // Validate the address.
 | |
|   if (addr == LLDB_INVALID_ADDRESS)
 | |
|     return Status("ProcessFreeBSD::%s invalid load address specified.",
 | |
|                   __FUNCTION__);
 | |
| 
 | |
|   Breakpoint *const sw_step_break =
 | |
|       m_process->GetTarget().CreateBreakpoint(addr, true, false).get();
 | |
|   sw_step_break->SetCallback(SingleStepBreakpointHit, this, true);
 | |
|   sw_step_break->SetBreakpointKind("software-signle-step");
 | |
| 
 | |
|   if (log)
 | |
|     log->Printf("ProcessFreeBSD::%s addr = 0x%" PRIx64 " -- SUCCESS",
 | |
|                 __FUNCTION__, addr);
 | |
| 
 | |
|   m_threads_stepping_with_breakpoint.insert({tid, sw_step_break->GetID()});
 | |
|   return Status();
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::IsSoftwareStepBreakpoint(lldb::tid_t tid) {
 | |
|   ThreadSP thread = GetThreadList().FindThreadByID(tid);
 | |
|   if (!thread)
 | |
|     return false;
 | |
| 
 | |
|   assert(thread->GetRegisterContext());
 | |
|   lldb::addr_t stop_pc = thread->GetRegisterContext()->GetPC();
 | |
| 
 | |
|   const auto &iter = m_threads_stepping_with_breakpoint.find(tid);
 | |
|   if (iter == m_threads_stepping_with_breakpoint.end())
 | |
|     return false;
 | |
| 
 | |
|   lldb::break_id_t bp_id = iter->second;
 | |
|   BreakpointSP bp = GetTarget().GetBreakpointByID(bp_id);
 | |
|   if (!bp)
 | |
|     return false;
 | |
| 
 | |
|   BreakpointLocationSP bp_loc = bp->FindLocationByAddress(stop_pc);
 | |
|   if (!bp_loc)
 | |
|     return false;
 | |
| 
 | |
|   GetTarget().RemoveBreakpointByID(bp_id);
 | |
|   m_threads_stepping_with_breakpoint.erase(tid);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool ProcessFreeBSD::SupportHardwareSingleStepping() const {
 | |
|   lldb_private::ArchSpec arch = GetTarget().GetArchitecture();
 | |
|   if (arch.GetMachine() == llvm::Triple::arm ||
 | |
|       arch.GetMachine() == llvm::Triple::mips64 ||
 | |
|       arch.GetMachine() == llvm::Triple::mips64el ||
 | |
|       arch.GetMachine() == llvm::Triple::mips ||
 | |
|       arch.GetMachine() == llvm::Triple::mipsel)
 | |
|     return false;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Status ProcessFreeBSD::SetupSoftwareSingleStepping(lldb::tid_t tid) {
 | |
|   std::unique_ptr<EmulateInstruction> emulator_ap(
 | |
|       EmulateInstruction::FindPlugin(GetTarget().GetArchitecture(),
 | |
|                                      eInstructionTypePCModifying, nullptr));
 | |
| 
 | |
|   if (emulator_ap == nullptr)
 | |
|     return Status("Instruction emulator not found!");
 | |
| 
 | |
|   FreeBSDThread *thread = static_cast<FreeBSDThread *>(
 | |
|       m_thread_list.FindThreadByID(tid, false).get());
 | |
|   if (thread == NULL)
 | |
|     return Status("Thread not found not found!");
 | |
| 
 | |
|   lldb::RegisterContextSP register_context_sp = thread->GetRegisterContext();
 | |
| 
 | |
|   EmulatorBaton baton(this, register_context_sp.get());
 | |
|   emulator_ap->SetBaton(&baton);
 | |
|   emulator_ap->SetReadMemCallback(&ReadMemoryCallback);
 | |
|   emulator_ap->SetReadRegCallback(&ReadRegisterCallback);
 | |
|   emulator_ap->SetWriteMemCallback(&WriteMemoryCallback);
 | |
|   emulator_ap->SetWriteRegCallback(&WriteRegisterCallback);
 | |
| 
 | |
|   if (!emulator_ap->ReadInstruction())
 | |
|     return Status("Read instruction failed!");
 | |
| 
 | |
|   bool emulation_result =
 | |
|       emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
 | |
|   const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo(
 | |
|       eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
 | |
|   auto pc_it =
 | |
|       baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
 | |
| 
 | |
|   lldb::addr_t next_pc;
 | |
|   if (emulation_result) {
 | |
|     assert(pc_it != baton.m_register_values.end() &&
 | |
|            "Emulation was successful but PC wasn't updated");
 | |
|     next_pc = pc_it->second.GetAsUInt64();
 | |
|   } else if (pc_it == baton.m_register_values.end()) {
 | |
|     // Emulate instruction failed and it haven't changed PC. Advance PC
 | |
|     // with the size of the current opcode because the emulation of all
 | |
|     // PC modifying instruction should be successful. The failure most
 | |
|     // likely caused by a not supported instruction which don't modify PC.
 | |
|     next_pc =
 | |
|         register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize();
 | |
|   } else {
 | |
|     // The instruction emulation failed after it modified the PC. It is an
 | |
|     // unknown error where we can't continue because the next instruction is
 | |
|     // modifying the PC but we don't  know how.
 | |
|     return Status("Instruction emulation failed unexpectedly");
 | |
|   }
 | |
| 
 | |
|   SetSoftwareSingleStepBreakpoint(tid, next_pc);
 | |
|   return Status();
 | |
| }
 |