| //===-- 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> |
| |
| // C++ Includes |
| #include <mutex> |
| |
| // Other libraries and framework includes |
| #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/Target.h" |
| |
| #include "ProcessFreeBSD.h" |
| #include "ProcessPOSIXLog.h" |
| #include "Plugins/Process/Utility/InferiorCallPOSIX.h" |
| #include "Plugins/Process/Utility/FreeBSDSignals.h" |
| #include "ProcessMonitor.h" |
| #include "FreeBSDThread.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/FileSpec.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/Host/posix/Fcntl.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(Target& target, |
| Listener &listener, |
| const FileSpec *crash_file_path) |
| { |
| lldb::ProcessSP process_sp; |
| if (crash_file_path == NULL) |
| process_sp.reset(new ProcessFreeBSD (target, listener, GetFreeBSDSignals())); |
| return process_sp; |
| } |
| |
| void |
| ProcessFreeBSD::Initialize() |
| { |
| static std::once_flag g_once_flag; |
| |
| std::call_once(g_once_flag, []() { |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), |
| GetPluginDescriptionStatic(), |
| CreateInstance); |
| ProcessPOSIXLog::Initialize(GetPluginNameStatic()); |
| }); |
| } |
| |
| 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::GetPluginCommandHelp(const char *command, Stream *strm) |
| { |
| } |
| |
| Error |
| ProcessFreeBSD::ExecutePluginCommand(Args &command, Stream *strm) |
| { |
| return Error(1, eErrorTypeGeneric); |
| } |
| |
| Log * |
| ProcessFreeBSD::EnablePluginLogging(Stream *strm, Args &command) |
| { |
| return NULL; |
| } |
| |
| void |
| ProcessFreeBSD::Terminate() |
| { |
| } |
| |
| Error |
| ProcessFreeBSD::DoDetach(bool keep_stopped) |
| { |
| Error 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; |
| } |
| |
| Error |
| ProcessFreeBSD::DoResume() |
| { |
| Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); |
| |
| SetPrivateState(eStateRunning); |
| |
| Mutex::Locker lock(m_thread_list.GetMutex()); |
| bool do_step = false; |
| |
| 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; |
| } |
| 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) |
| m_monitor->SingleStep(GetID(), m_resume_signo); |
| else |
| m_monitor->Resume(GetID(), m_resume_signo); |
| |
| return Error(); |
| } |
| |
| 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; |
| } |
| |
| Error |
| 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) |
| { |
| Mutex::Locker lock(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(Target& target, Listener &listener, UnixSignalsSP &unix_signals_sp) |
| : Process(target, listener, unix_signals_sp), |
| m_byte_order(lldb::endian::InlHostByteOrder()), |
| m_monitor(NULL), |
| m_module(NULL), |
| m_message_mutex (Mutex::eMutexTypeRecursive), |
| 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(Target &target, bool plugin_specified_by_name) |
| { |
| // For now we are just making sure the file exists for a given module |
| ModuleSP exe_module_sp(target.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; |
| } |
| |
| Error |
| ProcessFreeBSD::DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) |
| { |
| Error error; |
| assert(m_monitor == NULL); |
| |
| Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); |
| if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) |
| log->Printf ("ProcessFreeBSD::%s(pid = %" PRIi64 ")", __FUNCTION__, GetID()); |
| |
| m_monitor = new ProcessMonitor(this, pid, error); |
| |
| if (!error.Success()) |
| return error; |
| |
| PlatformSP platform_sp (m_target.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(), m_target.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() && !m_target.GetArchitecture().IsExactMatch(module_arch)) |
| m_target.SetArchitecture(module_arch); |
| |
| // Initialize the target module list |
| m_target.SetExecutableModule (exe_module_sp, true); |
| |
| SetSTDIOFileDescriptor(m_monitor->GetTerminalFD()); |
| |
| SetID(pid); |
| |
| return error; |
| } |
| |
| Error |
| ProcessFreeBSD::WillLaunch(Module* module) |
| { |
| Error 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; |
| } |
| |
| Error |
| ProcessFreeBSD::DoLaunch (Module *module, |
| ProcessLaunchInfo &launch_info) |
| { |
| Error error; |
| assert(m_monitor == NULL); |
| |
| FileSpec working_dir = launch_info.GetWorkingDirectory(); |
| if (working_dir && |
| (!working_dir.ResolvePath() || |
| working_dir.GetFileType() != FileSpec::eFileTypeDirectory)) |
| { |
| 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. |
| int stdio = fcntl(terminal, F_DUPFD_CLOEXEC, 0); |
| if (stdio == -1) { |
| error.SetErrorToErrno(); |
| return error; |
| } |
| 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; |
| } |
| |
| Error |
| ProcessFreeBSD::DoHalt(bool &caused_stop) |
| { |
| Error error; |
| |
| if (IsStopped()) |
| { |
| caused_stop = false; |
| } |
| else if (kill(GetID(), SIGSTOP)) |
| { |
| caused_stop = false; |
| error.SetErrorToErrno(); |
| } |
| else |
| { |
| caused_stop = true; |
| } |
| return error; |
| } |
| |
| Error |
| ProcessFreeBSD::DoSignal(int signal) |
| { |
| Error error; |
| |
| if (kill(GetID(), signal)) |
| error.SetErrorToErrno(); |
| |
| return error; |
| } |
| |
| Error |
| ProcessFreeBSD::DoDestroy() |
| { |
| Error 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()); |
| Error 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)); |
| if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) |
| log->Printf ("ProcessFreeBSD::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size()); |
| |
| Mutex::Locker lock(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(); |
| if (log) |
| log->Printf ("ProcessFreeBSD::%s(), message_queue size = %d, pid = %" PRIi64, __FUNCTION__, (int)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. |
| if (log) |
| log->Printf ("ProcessFreeBSD::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid); |
| |
| Mutex::Locker lock(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, Error &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, |
| Error &error) |
| { |
| assert(m_monitor); |
| return m_monitor->WriteMemory(vm_addr, buf, size, error); |
| } |
| |
| addr_t |
| ProcessFreeBSD::DoAllocateMemory(size_t size, uint32_t permissions, |
| Error &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; |
| } |
| |
| Error |
| ProcessFreeBSD::DoDeallocateMemory(lldb::addr_t addr) |
| { |
| Error 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; |
| } |
| |
| Error |
| ProcessFreeBSD::EnableBreakpointSite(BreakpointSite *bp_site) |
| { |
| return EnableSoftwareBreakpoint(bp_site); |
| } |
| |
| Error |
| ProcessFreeBSD::DisableBreakpointSite(BreakpointSite *bp_site) |
| { |
| return DisableSoftwareBreakpoint(bp_site); |
| } |
| |
| Error |
| ProcessFreeBSD::EnableWatchpoint(Watchpoint *wp, bool notify) |
| { |
| Error 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; |
| Mutex::Locker lock(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; |
| } |
| |
| Error |
| ProcessFreeBSD::DisableWatchpoint(Watchpoint *wp, bool notify) |
| { |
| Error 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; |
| Mutex::Locker lock(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; |
| } |
| |
| Error |
| ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num) |
| { |
| Error error; |
| Mutex::Locker lock(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; |
| } |
| |
| Error |
| ProcessFreeBSD::GetWatchpointSupportInfo(uint32_t &num, bool &after) |
| { |
| Error error = GetWatchpointSupportInfo(num); |
| // Watchpoints trigger and halt the inferior after |
| // the corresponding instruction has been executed. |
| after = true; |
| return error; |
| } |
| |
| uint32_t |
| ProcessFreeBSD::UpdateThreadListIfNeeded() |
| { |
| Mutex::Locker lock(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, Error &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; |
| Mutex::Locker lock(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 = m_target.GetPlatform (); |
| if (platform_sp && platform_sp->IsHost ()) |
| return lldb_private::Host::GetAuxvData(this); |
| |
| // Somewhat unexpected - the process is not running locally or we don't have a platform. |
| assert (false && "no platform or not the host - how did we get here with ProcessFreeBSD?"); |
| return DataBufferSP (); |
| } |