diff options
Diffstat (limited to 'source/Plugins/Process/Linux/NativeProcessLinux.cpp')
-rw-r--r-- | source/Plugins/Process/Linux/NativeProcessLinux.cpp | 2097 |
1 files changed, 0 insertions, 2097 deletions
diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp deleted file mode 100644 index 8c6c95380e81..000000000000 --- a/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ /dev/null @@ -1,2097 +0,0 @@ -//===-- NativeProcessLinux.cpp -------------------------------- -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "NativeProcessLinux.h" - -#include <errno.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> - -#include <fstream> -#include <mutex> -#include <sstream> -#include <string> -#include <unordered_map> - -#include "lldb/Core/EmulateInstruction.h" -#include "lldb/Core/ModuleSpec.h" -#include "lldb/Host/Host.h" -#include "lldb/Host/HostProcess.h" -#include "lldb/Host/PseudoTerminal.h" -#include "lldb/Host/ThreadLauncher.h" -#include "lldb/Host/common/NativeRegisterContext.h" -#include "lldb/Host/linux/Ptrace.h" -#include "lldb/Host/linux/Uio.h" -#include "lldb/Host/posix/ProcessLauncherPosixFork.h" -#include "lldb/Symbol/ObjectFile.h" -#include "lldb/Target/Process.h" -#include "lldb/Target/ProcessLaunchInfo.h" -#include "lldb/Target/Target.h" -#include "lldb/Utility/LLDBAssert.h" -#include "lldb/Utility/RegisterValue.h" -#include "lldb/Utility/State.h" -#include "lldb/Utility/Status.h" -#include "lldb/Utility/StringExtractor.h" -#include "llvm/Support/Errno.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Threading.h" - -#include "NativeThreadLinux.h" -#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" -#include "Plugins/Process/Utility/LinuxProcMaps.h" -#include "Procfs.h" - -#include <linux/unistd.h> -#include <sys/socket.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/user.h> -#include <sys/wait.h> - -// Support hardware breakpoints in case it has not been defined -#ifndef TRAP_HWBKPT -#define TRAP_HWBKPT 4 -#endif - -using namespace lldb; -using namespace lldb_private; -using namespace lldb_private::process_linux; -using namespace llvm; - -// Private bits we only need internally. - -static bool ProcessVmReadvSupported() { - static bool is_supported; - static llvm::once_flag flag; - - llvm::call_once(flag, [] { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - uint32_t source = 0x47424742; - uint32_t dest = 0; - - struct iovec local, remote; - remote.iov_base = &source; - local.iov_base = &dest; - remote.iov_len = local.iov_len = sizeof source; - - // We shall try if cross-process-memory reads work by attempting to read a - // value from our own process. - ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); - is_supported = (res == sizeof(source) && source == dest); - if (is_supported) - LLDB_LOG(log, - "Detected kernel support for process_vm_readv syscall. " - "Fast memory reads enabled."); - else - LLDB_LOG(log, - "syscall process_vm_readv failed (error: {0}). Fast memory " - "reads disabled.", - llvm::sys::StrError()); - }); - - return is_supported; -} - -namespace { -void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - if (!log) - return; - - if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) - LLDB_LOG(log, "setting STDIN to '{0}'", action->GetFileSpec()); - else - LLDB_LOG(log, "leaving STDIN as is"); - - if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) - LLDB_LOG(log, "setting STDOUT to '{0}'", action->GetFileSpec()); - else - LLDB_LOG(log, "leaving STDOUT as is"); - - if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) - LLDB_LOG(log, "setting STDERR to '{0}'", action->GetFileSpec()); - else - LLDB_LOG(log, "leaving STDERR as is"); - - int i = 0; - for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; - ++args, ++i) - LLDB_LOG(log, "arg {0}: '{1}'", i, *args); -} - -void DisplayBytes(StreamString &s, void *bytes, uint32_t count) { - uint8_t *ptr = (uint8_t *)bytes; - const uint32_t loop_count = std::min<uint32_t>(DEBUG_PTRACE_MAXBYTES, count); - for (uint32_t i = 0; i < loop_count; i++) { - s.Printf("[%x]", *ptr); - ptr++; - } -} - -void PtraceDisplayBytes(int &req, void *data, size_t data_size) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - if (!log) - return; - StreamString buf; - - switch (req) { - case PTRACE_POKETEXT: { - DisplayBytes(buf, &data, 8); - LLDB_LOGV(log, "PTRACE_POKETEXT {0}", buf.GetData()); - break; - } - case PTRACE_POKEDATA: { - DisplayBytes(buf, &data, 8); - LLDB_LOGV(log, "PTRACE_POKEDATA {0}", buf.GetData()); - break; - } - case PTRACE_POKEUSER: { - DisplayBytes(buf, &data, 8); - LLDB_LOGV(log, "PTRACE_POKEUSER {0}", buf.GetData()); - break; - } - case PTRACE_SETREGS: { - DisplayBytes(buf, data, data_size); - LLDB_LOGV(log, "PTRACE_SETREGS {0}", buf.GetData()); - break; - } - case PTRACE_SETFPREGS: { - DisplayBytes(buf, data, data_size); - LLDB_LOGV(log, "PTRACE_SETFPREGS {0}", buf.GetData()); - break; - } - case PTRACE_SETSIGINFO: { - DisplayBytes(buf, data, sizeof(siginfo_t)); - LLDB_LOGV(log, "PTRACE_SETSIGINFO {0}", buf.GetData()); - break; - } - case PTRACE_SETREGSET: { - // Extract iov_base from data, which is a pointer to the struct iovec - DisplayBytes(buf, *(void **)data, data_size); - LLDB_LOGV(log, "PTRACE_SETREGSET {0}", buf.GetData()); - break; - } - default: {} - } -} - -static constexpr unsigned k_ptrace_word_size = sizeof(void *); -static_assert(sizeof(long) >= k_ptrace_word_size, - "Size of long must be larger than ptrace word size"); -} // end of anonymous namespace - -// Simple helper function to ensure flags are enabled on the given file -// descriptor. -static Status EnsureFDFlags(int fd, int flags) { - Status error; - - int status = fcntl(fd, F_GETFL); - if (status == -1) { - error.SetErrorToErrno(); - return error; - } - - if (fcntl(fd, F_SETFL, status | flags) == -1) { - error.SetErrorToErrno(); - return error; - } - - return error; -} - -// ----------------------------------------------------------------------------- -// Public Static Methods -// ----------------------------------------------------------------------------- - -llvm::Expected<std::unique_ptr<NativeProcessProtocol>> -NativeProcessLinux::Factory::Launch(ProcessLaunchInfo &launch_info, - NativeDelegate &native_delegate, - MainLoop &mainloop) const { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - MaybeLogLaunchInfo(launch_info); - - Status status; - ::pid_t pid = ProcessLauncherPosixFork() - .LaunchProcess(launch_info, status) - .GetProcessId(); - LLDB_LOG(log, "pid = {0:x}", pid); - if (status.Fail()) { - LLDB_LOG(log, "failed to launch process: {0}", status); - return status.ToError(); - } - - // Wait for the child process to trap on its call to execve. - int wstatus; - ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); - assert(wpid == pid); - (void)wpid; - if (!WIFSTOPPED(wstatus)) { - LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", - WaitStatus::Decode(wstatus)); - return llvm::make_error<StringError>("Could not sync with inferior process", - llvm::inconvertibleErrorCode()); - } - LLDB_LOG(log, "inferior started, now in stopped state"); - - ProcessInstanceInfo Info; - if (!Host::GetProcessInfo(pid, Info)) { - return llvm::make_error<StringError>("Cannot get process architecture", - llvm::inconvertibleErrorCode()); - } - - // Set the architecture to the exe architecture. - LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, - Info.GetArchitecture().GetArchitectureName()); - - status = SetDefaultPtraceOpts(pid); - if (status.Fail()) { - LLDB_LOG(log, "failed to set default ptrace options: {0}", status); - return status.ToError(); - } - - return std::unique_ptr<NativeProcessLinux>(new NativeProcessLinux( - pid, launch_info.GetPTY().ReleaseMasterFileDescriptor(), native_delegate, - Info.GetArchitecture(), mainloop, {pid})); -} - -llvm::Expected<std::unique_ptr<NativeProcessProtocol>> -NativeProcessLinux::Factory::Attach( - lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, - MainLoop &mainloop) const { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "pid = {0:x}", pid); - - // Retrieve the architecture for the running process. - ProcessInstanceInfo Info; - if (!Host::GetProcessInfo(pid, Info)) { - return llvm::make_error<StringError>("Cannot get process architecture", - llvm::inconvertibleErrorCode()); - } - - auto tids_or = NativeProcessLinux::Attach(pid); - if (!tids_or) - return tids_or.takeError(); - - return std::unique_ptr<NativeProcessLinux>(new NativeProcessLinux( - pid, -1, native_delegate, Info.GetArchitecture(), mainloop, *tids_or)); -} - -// ----------------------------------------------------------------------------- -// Public Instance Methods -// ----------------------------------------------------------------------------- - -NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd, - NativeDelegate &delegate, - const ArchSpec &arch, MainLoop &mainloop, - llvm::ArrayRef<::pid_t> tids) - : NativeProcessProtocol(pid, terminal_fd, delegate), m_arch(arch) { - if (m_terminal_fd != -1) { - Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); - assert(status.Success()); - } - - Status status; - m_sigchld_handle = mainloop.RegisterSignal( - SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); - assert(m_sigchld_handle && status.Success()); - - for (const auto &tid : tids) { - NativeThreadLinux &thread = AddThread(tid); - thread.SetStoppedBySignal(SIGSTOP); - ThreadWasCreated(thread); - } - - // Let our process instance know the thread has stopped. - SetCurrentThreadID(tids[0]); - SetState(StateType::eStateStopped, false); - - // Proccess any signals we received before installing our handler - SigchldHandler(); -} - -llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - Status status; - // Use a map to keep track of the threads which we have attached/need to - // attach. - Host::TidMap tids_to_attach; - while (Host::FindProcessThreads(pid, tids_to_attach)) { - for (Host::TidMap::iterator it = tids_to_attach.begin(); - it != tids_to_attach.end();) { - if (it->second == false) { - lldb::tid_t tid = it->first; - - // Attach to the requested process. - // An attach will cause the thread to stop with a SIGSTOP. - if ((status = PtraceWrapper(PTRACE_ATTACH, tid)).Fail()) { - // No such thread. The thread may have exited. More error handling - // may be needed. - if (status.GetError() == ESRCH) { - it = tids_to_attach.erase(it); - continue; - } - return status.ToError(); - } - - int wpid = - llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL); - // Need to use __WALL otherwise we receive an error with errno=ECHLD At - // this point we should have a thread stopped if waitpid succeeds. - if (wpid < 0) { - // No such thread. The thread may have exited. More error handling - // may be needed. - if (errno == ESRCH) { - it = tids_to_attach.erase(it); - continue; - } - return llvm::errorCodeToError( - std::error_code(errno, std::generic_category())); - } - - if ((status = SetDefaultPtraceOpts(tid)).Fail()) - return status.ToError(); - - LLDB_LOG(log, "adding tid = {0}", tid); - it->second = true; - } - - // move the loop forward - ++it; - } - } - - size_t tid_count = tids_to_attach.size(); - if (tid_count == 0) - return llvm::make_error<StringError>("No such process", - llvm::inconvertibleErrorCode()); - - std::vector<::pid_t> tids; - tids.reserve(tid_count); - for (const auto &p : tids_to_attach) - tids.push_back(p.first); - return std::move(tids); -} - -Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { - long ptrace_opts = 0; - - // Have the child raise an event on exit. This is used to keep the child in - // limbo until it is destroyed. - ptrace_opts |= PTRACE_O_TRACEEXIT; - - // Have the tracer trace threads which spawn in the inferior process. - // TODO: if we want to support tracing the inferiors' child, add the - // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) - ptrace_opts |= PTRACE_O_TRACECLONE; - - // Have the tracer notify us before execve returns (needed to disable legacy - // SIGTRAP generation) - ptrace_opts |= PTRACE_O_TRACEEXEC; - - return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); -} - -// Handles all waitpid events from the inferior process. -void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited, - WaitStatus status) { - Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // Certain activities differ based on whether the pid is the tid of the main - // thread. - const bool is_main_thread = (pid == GetID()); - - // Handle when the thread exits. - if (exited) { - LLDB_LOG(log, - "got exit signal({0}) , tid = {1} ({2} main thread), process " - "state = {3}", - signal, pid, is_main_thread ? "is" : "is not", GetState()); - - // This is a thread that exited. Ensure we're not tracking it anymore. - StopTrackingThread(pid); - - if (is_main_thread) { - // The main thread exited. We're done monitoring. Report to delegate. - SetExitStatus(status, true); - - // Notify delegate that our process has exited. - SetState(StateType::eStateExited, true); - } - return; - } - - siginfo_t info; - const auto info_err = GetSignalInfo(pid, &info); - auto thread_sp = GetThreadByID(pid); - - if (!thread_sp) { - // Normally, the only situation when we cannot find the thread is if we - // have just received a new thread notification. This is indicated by - // GetSignalInfo() returning si_code == SI_USER and si_pid == 0 - LLDB_LOG(log, "received notification about an unknown tid {0}.", pid); - - if (info_err.Fail()) { - LLDB_LOG(log, - "(tid {0}) GetSignalInfo failed ({1}). " - "Ingoring this notification.", - pid, info_err); - return; - } - - LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code, - info.si_pid); - - NativeThreadLinux &thread = AddThread(pid); - - // Resume the newly created thread. - ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); - ThreadWasCreated(thread); - return; - } - - // Get details on the signal raised. - if (info_err.Success()) { - // We have retrieved the signal info. Dispatch appropriately. - if (info.si_signo == SIGTRAP) - MonitorSIGTRAP(info, *thread_sp); - else - MonitorSignal(info, *thread_sp, exited); - } else { - if (info_err.GetError() == EINVAL) { - // This is a group stop reception for this tid. We can reach here if we - // reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU into the tracee, - // triggering the group-stop mechanism. Normally receiving these would - // stop the process, pending a SIGCONT. Simulating this state in a - // debugger is hard and is generally not needed (one use case is - // debugging background task being managed by a shell). For general use, - // it is sufficient to stop the process in a signal-delivery stop which - // happens before the group stop. This done by MonitorSignal and works - // correctly for all signals. - LLDB_LOG(log, - "received a group stop for pid {0} tid {1}. Transparent " - "handling of group stops not supported, resuming the " - "thread.", - GetID(), pid); - ResumeThread(*thread_sp, thread_sp->GetState(), - LLDB_INVALID_SIGNAL_NUMBER); - } else { - // ptrace(GETSIGINFO) failed (but not due to group-stop). - - // A return value of ESRCH means the thread/process is no longer on the - // system, so it was killed somehow outside of our control. Either way, - // we can't do anything with it anymore. - - // Stop tracking the metadata for the thread since it's entirely off the - // system now. - const bool thread_found = StopTrackingThread(pid); - - LLDB_LOG(log, - "GetSignalInfo failed: {0}, tid = {1}, signal = {2}, " - "status = {3}, main_thread = {4}, thread_found: {5}", - info_err, pid, signal, status, is_main_thread, thread_found); - - if (is_main_thread) { - // Notify the delegate - our process is not available but appears to - // have been killed outside our control. Is eStateExited the right - // exit state in this case? - SetExitStatus(status, true); - SetState(StateType::eStateExited, true); - } else { - // This thread was pulled out from underneath us. Anything to do here? - // Do we want to do an all stop? - LLDB_LOG(log, - "pid {0} tid {1} non-main thread exit occurred, didn't " - "tell delegate anything since thread disappeared out " - "from underneath us", - GetID(), pid); - } - } - } -} - -void NativeProcessLinux::WaitForNewThread(::pid_t tid) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - if (GetThreadByID(tid)) { - // We are already tracking the thread - we got the event on the new thread - // (see MonitorSignal) before this one. We are done. - return; - } - - // The thread is not tracked yet, let's wait for it to appear. - int status = -1; - LLDB_LOG(log, - "received thread creation event for tid {0}. tid not tracked " - "yet, waiting for thread to appear...", - tid); - ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, &status, __WALL); - // Since we are waiting on a specific tid, this must be the creation event. - // But let's do some checks just in case. - if (wait_pid != tid) { - LLDB_LOG(log, - "waiting for tid {0} failed. Assuming the thread has " - "disappeared in the meantime", - tid); - // The only way I know of this could happen is if the whole process was - // SIGKILLed in the mean time. In any case, we can't do anything about that - // now. - return; - } - if (WIFEXITED(status)) { - LLDB_LOG(log, - "waiting for tid {0} returned an 'exited' event. Not " - "tracking the thread.", - tid); - // Also a very improbable event. - return; - } - - LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid); - NativeThreadLinux &new_thread = AddThread(tid); - - ResumeThread(new_thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); - ThreadWasCreated(new_thread); -} - -void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, - NativeThreadLinux &thread) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - const bool is_main_thread = (thread.GetID() == GetID()); - - assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); - - switch (info.si_code) { - // TODO: these two cases are required if we want to support tracing of the - // inferiors' children. We'd need this to debug a monitor. case (SIGTRAP | - // (PTRACE_EVENT_FORK << 8)): case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): - - case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): { - // This is the notification on the parent thread which informs us of new - // thread creation. We don't want to do anything with the parent thread so - // we just resume it. In case we want to implement "break on thread - // creation" functionality, we would need to stop here. - - unsigned long event_message = 0; - if (GetEventMessage(thread.GetID(), &event_message).Fail()) { - LLDB_LOG(log, - "pid {0} received thread creation event but " - "GetEventMessage failed so we don't know the new tid", - thread.GetID()); - } else - WaitForNewThread(event_message); - - ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); - break; - } - - case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): { - LLDB_LOG(log, "received exec event, code = {0}", info.si_code ^ SIGTRAP); - - // Exec clears any pending notifications. - m_pending_notification_tid = LLDB_INVALID_THREAD_ID; - - // Remove all but the main thread here. Linux fork creates a new process - // which only copies the main thread. - LLDB_LOG(log, "exec received, stop tracking all but main thread"); - - for (auto i = m_threads.begin(); i != m_threads.end();) { - if ((*i)->GetID() == GetID()) - i = m_threads.erase(i); - else - ++i; - } - assert(m_threads.size() == 1); - auto *main_thread = static_cast<NativeThreadLinux *>(m_threads[0].get()); - - SetCurrentThreadID(main_thread->GetID()); - main_thread->SetStoppedByExec(); - - // Tell coordinator about about the "new" (since exec) stopped main thread. - ThreadWasCreated(*main_thread); - - // Let our delegate know we have just exec'd. - NotifyDidExec(); - - // Let the process know we're stopped. - StopRunningThreads(main_thread->GetID()); - - break; - } - - case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): { - // The inferior process or one of its threads is about to exit. We don't - // want to do anything with the thread so we just resume it. In case we - // want to implement "break on thread exit" functionality, we would need to - // stop here. - - unsigned long data = 0; - if (GetEventMessage(thread.GetID(), &data).Fail()) - data = -1; - - LLDB_LOG(log, - "received PTRACE_EVENT_EXIT, data = {0:x}, WIFEXITED={1}, " - "WIFSIGNALED={2}, pid = {3}, main_thread = {4}", - data, WIFEXITED(data), WIFSIGNALED(data), thread.GetID(), - is_main_thread); - - - StateType state = thread.GetState(); - if (!StateIsRunningState(state)) { - // Due to a kernel bug, we may sometimes get this stop after the inferior - // gets a SIGKILL. This confuses our state tracking logic in - // ResumeThread(), since normally, we should not be receiving any ptrace - // events while the inferior is stopped. This makes sure that the - // inferior is resumed and exits normally. - state = eStateRunning; - } - ResumeThread(thread, state, LLDB_INVALID_SIGNAL_NUMBER); - - break; - } - - case 0: - case TRAP_TRACE: // We receive this on single stepping. - case TRAP_HWBKPT: // We receive this on watchpoint hit - { - // If a watchpoint was hit, report it - uint32_t wp_index; - Status error = thread.GetRegisterContext().GetWatchpointHitIndex( - wp_index, (uintptr_t)info.si_addr); - if (error.Fail()) - LLDB_LOG(log, - "received error while checking for watchpoint hits, pid = " - "{0}, error = {1}", - thread.GetID(), error); - if (wp_index != LLDB_INVALID_INDEX32) { - MonitorWatchpoint(thread, wp_index); - break; - } - - // If a breakpoint was hit, report it - uint32_t bp_index; - error = thread.GetRegisterContext().GetHardwareBreakHitIndex( - bp_index, (uintptr_t)info.si_addr); - if (error.Fail()) - LLDB_LOG(log, "received error while checking for hardware " - "breakpoint hits, pid = {0}, error = {1}", - thread.GetID(), error); - if (bp_index != LLDB_INVALID_INDEX32) { - MonitorBreakpoint(thread); - break; - } - - // Otherwise, report step over - MonitorTrace(thread); - break; - } - - case SI_KERNEL: -#if defined __mips__ - // For mips there is no special signal for watchpoint So we check for - // watchpoint in kernel trap - { - // If a watchpoint was hit, report it - uint32_t wp_index; - Status error = thread.GetRegisterContext().GetWatchpointHitIndex( - wp_index, LLDB_INVALID_ADDRESS); - if (error.Fail()) - LLDB_LOG(log, - "received error while checking for watchpoint hits, pid = " - "{0}, error = {1}", - thread.GetID(), error); - if (wp_index != LLDB_INVALID_INDEX32) { - MonitorWatchpoint(thread, wp_index); - break; - } - } -// NO BREAK -#endif - case TRAP_BRKPT: - MonitorBreakpoint(thread); - break; - - case SIGTRAP: - case (SIGTRAP | 0x80): - LLDB_LOG( - log, - "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}, resuming", - info.si_code, GetID(), thread.GetID()); - - // Ignore these signals until we know more about them. - ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); - break; - - default: - LLDB_LOG(log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}", - info.si_code, GetID(), thread.GetID()); - MonitorSignal(info, thread, false); - break; - } -} - -void NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "received trace event, pid = {0}", thread.GetID()); - - // This thread is currently stopped. - thread.SetStoppedByTrace(); - - StopRunningThreads(thread.GetID()); -} - -void NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) { - Log *log( - GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); - LLDB_LOG(log, "received breakpoint event, pid = {0}", thread.GetID()); - - // Mark the thread as stopped at breakpoint. - thread.SetStoppedByBreakpoint(); - FixupBreakpointPCAsNeeded(thread); - - if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != - m_threads_stepping_with_breakpoint.end()) - thread.SetStoppedByTrace(); - - StopRunningThreads(thread.GetID()); -} - -void NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, - uint32_t wp_index) { - Log *log( - GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS)); - LLDB_LOG(log, "received watchpoint event, pid = {0}, wp_index = {1}", - thread.GetID(), wp_index); - - // Mark the thread as stopped at watchpoint. The address is at - // (lldb::addr_t)info->si_addr if we need it. - thread.SetStoppedByWatchpoint(wp_index); - - // We need to tell all other running threads before we notify the delegate - // about this stop. - StopRunningThreads(thread.GetID()); -} - -void NativeProcessLinux::MonitorSignal(const siginfo_t &info, - NativeThreadLinux &thread, bool exited) { - const int signo = info.si_signo; - const bool is_from_llgs = info.si_pid == getpid(); - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - // POSIX says that process behaviour is undefined after it ignores a SIGFPE, - // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a kill(2) - // or raise(3). Similarly for tgkill(2) on Linux. - // - // IOW, user generated signals never generate what we consider to be a - // "crash". - // - // Similarly, ACK signals generated by this monitor. - - // Handle the signal. - LLDB_LOG(log, - "received signal {0} ({1}) with code {2}, (siginfo pid = {3}, " - "waitpid pid = {4})", - Host::GetSignalAsCString(signo), signo, info.si_code, - thread.GetID()); - - // Check for thread stop notification. - if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP)) { - // This is a tgkill()-based stop. - LLDB_LOG(log, "pid {0} tid {1}, thread stopped", GetID(), thread.GetID()); - - // Check that we're not already marked with a stop reason. Note this thread - // really shouldn't already be marked as stopped - if we were, that would - // imply that the kernel signaled us with the thread stopping which we - // handled and marked as stopped, and that, without an intervening resume, - // we received another stop. It is more likely that we are missing the - // marking of a run state somewhere if we find that the thread was marked - // as stopped. - const StateType thread_state = thread.GetState(); - if (!StateIsStoppedState(thread_state, false)) { - // An inferior thread has stopped because of a SIGSTOP we have sent it. - // Generally, these are not important stops and we don't want to report - // them as they are just used to stop other threads when one thread (the - // one with the *real* stop reason) hits a breakpoint (watchpoint, - // etc...). However, in the case of an asynchronous Interrupt(), this - // *is* the real stop reason, so we leave the signal intact if this is - // the thread that was chosen as the triggering thread. - if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { - if (m_pending_notification_tid == thread.GetID()) - thread.SetStoppedBySignal(SIGSTOP, &info); - else - thread.SetStoppedWithNoReason(); - - SetCurrentThreadID(thread.GetID()); - SignalIfAllThreadsStopped(); - } else { - // We can end up here if stop was initiated by LLGS but by this time a - // thread stop has occurred - maybe initiated by another event. - Status error = ResumeThread(thread, thread.GetState(), 0); - if (error.Fail()) - LLDB_LOG(log, "failed to resume thread {0}: {1}", thread.GetID(), - error); - } - } else { - LLDB_LOG(log, - "pid {0} tid {1}, thread was already marked as a stopped " - "state (state={2}), leaving stop signal as is", - GetID(), thread.GetID(), thread_state); - SignalIfAllThreadsStopped(); - } - - // Done handling. - return; - } - - // Check if debugger should stop at this signal or just ignore it and resume - // the inferior. - if (m_signals_to_ignore.find(signo) != m_signals_to_ignore.end()) { - ResumeThread(thread, thread.GetState(), signo); - return; - } - - // This thread is stopped. - LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); - thread.SetStoppedBySignal(signo, &info); - - // Send a stop to the debugger after we get all other threads to stop. - StopRunningThreads(thread.GetID()); -} - -namespace { - -struct EmulatorBaton { - NativeProcessLinux &m_process; - NativeRegisterContext &m_reg_context; - - // eRegisterKindDWARF -> RegsiterValue - std::unordered_map<uint32_t, RegisterValue> m_register_values; - - EmulatorBaton(NativeProcessLinux &process, NativeRegisterContext ®_context) - : m_process(process), m_reg_context(reg_context) {} -}; - -} // anonymous namespace - -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); - - size_t bytes_read; - emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read); - 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 fill in the dwarf regsiter numbers (and in some case 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]); - - Status error = - emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value); - if (error.Success()) - return true; - - return false; -} - -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; -} - -static lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) { - const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); - return regsiter_context.ReadRegisterAsUnsigned(flags_info, - LLDB_INVALID_ADDRESS); -} - -Status -NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread) { - Status error; - NativeRegisterContext& register_context = thread.GetRegisterContext(); - - std::unique_ptr<EmulateInstruction> emulator_ap( - EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, - nullptr)); - - if (emulator_ap == nullptr) - return Status("Instruction emulator not found!"); - - EmulatorBaton baton(*this, register_context); - 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.GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); - const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo( - eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); - - auto pc_it = - baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); - auto flags_it = - baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]); - - lldb::addr_t next_pc; - lldb::addr_t next_flags; - if (emulation_result) { - assert(pc_it != baton.m_register_values.end() && - "Emulation was successfull but PC wasn't updated"); - next_pc = pc_it->second.GetAsUInt64(); - - if (flags_it != baton.m_register_values.end()) - next_flags = flags_it->second.GetAsUInt64(); - else - next_flags = ReadFlags(register_context); - } 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.GetPC() + emulator_ap->GetOpcode().GetByteSize(); - next_flags = ReadFlags(register_context); - } 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."); - } - - if (m_arch.GetMachine() == llvm::Triple::arm) { - if (next_flags & 0x20) { - // Thumb mode - error = SetSoftwareBreakpoint(next_pc, 2); - } else { - // Arm mode - error = SetSoftwareBreakpoint(next_pc, 4); - } - } else if (m_arch.GetMachine() == llvm::Triple::mips64 || - m_arch.GetMachine() == llvm::Triple::mips64el || - m_arch.GetMachine() == llvm::Triple::mips || - m_arch.GetMachine() == llvm::Triple::mipsel || - m_arch.GetMachine() == llvm::Triple::ppc64le) - error = SetSoftwareBreakpoint(next_pc, 4); - else { - // No size hint is given for the next breakpoint - error = SetSoftwareBreakpoint(next_pc, 0); - } - - // If setting the breakpoint fails because next_pc is out of the address - // space, ignore it and let the debugee segfault. - if (error.GetError() == EIO || error.GetError() == EFAULT) { - return Status(); - } else if (error.Fail()) - return error; - - m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); - - return Status(); -} - -bool NativeProcessLinux::SupportHardwareSingleStepping() const { - if (m_arch.GetMachine() == llvm::Triple::arm || - m_arch.GetMachine() == llvm::Triple::mips64 || - m_arch.GetMachine() == llvm::Triple::mips64el || - m_arch.GetMachine() == llvm::Triple::mips || - m_arch.GetMachine() == llvm::Triple::mipsel) - return false; - return true; -} - -Status NativeProcessLinux::Resume(const ResumeActionList &resume_actions) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "pid {0}", GetID()); - - bool software_single_step = !SupportHardwareSingleStepping(); - - if (software_single_step) { - for (const auto &thread : m_threads) { - assert(thread && "thread list should not contain NULL threads"); - - const ResumeAction *const action = - resume_actions.GetActionForThread(thread->GetID(), true); - if (action == nullptr) - continue; - - if (action->state == eStateStepping) { - Status error = SetupSoftwareSingleStepping( - static_cast<NativeThreadLinux &>(*thread)); - if (error.Fail()) - return error; - } - } - } - - for (const auto &thread : m_threads) { - assert(thread && "thread list should not contain NULL threads"); - - const ResumeAction *const action = - resume_actions.GetActionForThread(thread->GetID(), true); - - if (action == nullptr) { - LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), - thread->GetID()); - continue; - } - - LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}", - action->state, GetID(), thread->GetID()); - - switch (action->state) { - case eStateRunning: - case eStateStepping: { - // Run the thread, possibly feeding it the signal. - const int signo = action->signal; - ResumeThread(static_cast<NativeThreadLinux &>(*thread), action->state, - signo); - break; - } - - case eStateSuspended: - case eStateStopped: - llvm_unreachable("Unexpected state"); - - default: - return Status("NativeProcessLinux::%s (): unexpected state %s specified " - "for pid %" PRIu64 ", tid %" PRIu64, - __FUNCTION__, StateAsCString(action->state), GetID(), - thread->GetID()); - } - } - - return Status(); -} - -Status NativeProcessLinux::Halt() { - Status error; - - if (kill(GetID(), SIGSTOP) != 0) - error.SetErrorToErrno(); - - return error; -} - -Status NativeProcessLinux::Detach() { - Status error; - - // Stop monitoring the inferior. - m_sigchld_handle.reset(); - - // Tell ptrace to detach from the process. - if (GetID() == LLDB_INVALID_PROCESS_ID) - return error; - - for (const auto &thread : m_threads) { - Status e = Detach(thread->GetID()); - if (e.Fail()) - error = - e; // Save the error, but still attempt to detach from other threads. - } - - m_processor_trace_monitor.clear(); - m_pt_proces_trace_id = LLDB_INVALID_UID; - - return error; -} - -Status NativeProcessLinux::Signal(int signo) { - Status error; - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "sending signal {0} ({1}) to pid {1}", signo, - Host::GetSignalAsCString(signo), GetID()); - - if (kill(GetID(), signo)) - error.SetErrorToErrno(); - - return error; -} - -Status NativeProcessLinux::Interrupt() { - // Pick a running thread (or if none, a not-dead stopped thread) as the - // chosen thread that will be the stop-reason thread. - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - NativeThreadProtocol *running_thread = nullptr; - NativeThreadProtocol *stopped_thread = nullptr; - - LLDB_LOG(log, "selecting running thread for interrupt target"); - for (const auto &thread : m_threads) { - // If we have a running or stepping thread, we'll call that the target of - // the interrupt. - const auto thread_state = thread->GetState(); - if (thread_state == eStateRunning || thread_state == eStateStepping) { - running_thread = thread.get(); - break; - } else if (!stopped_thread && StateIsStoppedState(thread_state, true)) { - // Remember the first non-dead stopped thread. We'll use that as a - // backup if there are no running threads. - stopped_thread = thread.get(); - } - } - - if (!running_thread && !stopped_thread) { - Status error("found no running/stepping or live stopped threads as target " - "for interrupt"); - LLDB_LOG(log, "skipping due to error: {0}", error); - - return error; - } - - NativeThreadProtocol *deferred_signal_thread = - running_thread ? running_thread : stopped_thread; - - LLDB_LOG(log, "pid {0} {1} tid {2} chosen for interrupt target", GetID(), - running_thread ? "running" : "stopped", - deferred_signal_thread->GetID()); - - StopRunningThreads(deferred_signal_thread->GetID()); - - return Status(); -} - -Status NativeProcessLinux::Kill() { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "pid {0}", GetID()); - - Status error; - - switch (m_state) { - case StateType::eStateInvalid: - case StateType::eStateExited: - case StateType::eStateCrashed: - case StateType::eStateDetached: - case StateType::eStateUnloaded: - // Nothing to do - the process is already dead. - LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), - m_state); - return error; - - case StateType::eStateConnected: - case StateType::eStateAttaching: - case StateType::eStateLaunching: - case StateType::eStateStopped: - case StateType::eStateRunning: - case StateType::eStateStepping: - case StateType::eStateSuspended: - // We can try to kill a process in these states. - break; - } - - if (kill(GetID(), SIGKILL) != 0) { - error.SetErrorToErrno(); - return error; - } - - return error; -} - -Status NativeProcessLinux::GetMemoryRegionInfo(lldb::addr_t load_addr, - MemoryRegionInfo &range_info) { - // FIXME review that the final memory region returned extends to the end of - // the virtual address space, - // with no perms if it is not mapped. - - // Use an approach that reads memory regions from /proc/{pid}/maps. Assume - // proc maps entries are in ascending order. - // FIXME assert if we find differently. - - if (m_supports_mem_region == LazyBool::eLazyBoolNo) { - // We're done. - return Status("unsupported"); - } - - Status error = PopulateMemoryRegionCache(); - if (error.Fail()) { - return error; - } - - lldb::addr_t prev_base_address = 0; - - // FIXME start by finding the last region that is <= target address using - // binary search. Data is sorted. - // There can be a ton of regions on pthreads apps with lots of threads. - for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); - ++it) { - MemoryRegionInfo &proc_entry_info = it->first; - - // Sanity check assumption that /proc/{pid}/maps entries are ascending. - assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && - "descending /proc/pid/maps entries detected, unexpected"); - prev_base_address = proc_entry_info.GetRange().GetRangeBase(); - UNUSED_IF_ASSERT_DISABLED(prev_base_address); - - // If the target address comes before this entry, indicate distance to next - // region. - if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { - range_info.GetRange().SetRangeBase(load_addr); - range_info.GetRange().SetByteSize( - proc_entry_info.GetRange().GetRangeBase() - load_addr); - range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); - range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); - range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); - range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); - - return error; - } else if (proc_entry_info.GetRange().Contains(load_addr)) { - // The target address is within the memory region we're processing here. - range_info = proc_entry_info; - return error; - } - - // The target memory address comes somewhere after the region we just - // parsed. - } - - // If we made it here, we didn't find an entry that contained the given - // address. Return the load_addr as start and the amount of bytes betwwen - // load address and the end of the memory as size. - range_info.GetRange().SetRangeBase(load_addr); - range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); - range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); - range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); - range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); - range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); - return error; -} - -Status NativeProcessLinux::PopulateMemoryRegionCache() { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - // If our cache is empty, pull the latest. There should always be at least - // one memory region if memory region handling is supported. - if (!m_mem_region_cache.empty()) { - LLDB_LOG(log, "reusing {0} cached memory region entries", - m_mem_region_cache.size()); - return Status(); - } - - auto BufferOrError = getProcFile(GetID(), "maps"); - if (!BufferOrError) { - m_supports_mem_region = LazyBool::eLazyBoolNo; - return BufferOrError.getError(); - } - Status Result; - ParseLinuxMapRegions(BufferOrError.get()->getBuffer(), - [&](const MemoryRegionInfo &Info, const Status &ST) { - if (ST.Success()) { - FileSpec file_spec(Info.GetName().GetCString()); - FileSystem::Instance().Resolve(file_spec); - m_mem_region_cache.emplace_back(Info, file_spec); - return true; - } else { - m_supports_mem_region = LazyBool::eLazyBoolNo; - LLDB_LOG(log, "failed to parse proc maps: {0}", ST); - Result = ST; - return false; - } - }); - if (Result.Fail()) - return Result; - - if (m_mem_region_cache.empty()) { - // No entries after attempting to read them. This shouldn't happen if - // /proc/{pid}/maps is supported. Assume we don't support map entries via - // procfs. - m_supports_mem_region = LazyBool::eLazyBoolNo; - LLDB_LOG(log, - "failed to find any procfs maps entries, assuming no support " - "for memory region metadata retrieval"); - return Status("not supported"); - } - - LLDB_LOG(log, "read {0} memory region entries from /proc/{1}/maps", - m_mem_region_cache.size(), GetID()); - - // We support memory retrieval, remember that. - m_supports_mem_region = LazyBool::eLazyBoolYes; - return Status(); -} - -void NativeProcessLinux::DoStopIDBumped(uint32_t newBumpId) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, "newBumpId={0}", newBumpId); - LLDB_LOG(log, "clearing {0} entries from memory region cache", - m_mem_region_cache.size()); - m_mem_region_cache.clear(); -} - -Status NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, - lldb::addr_t &addr) { -// FIXME implementing this requires the equivalent of -// InferiorCallPOSIX::InferiorCallMmap, which depends on functional ThreadPlans -// working with Native*Protocol. -#if 1 - return Status("not implemented yet"); -#else - 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; - - // TODO implement this directly in NativeProcessLinux - // (and lift to NativeProcessPOSIX if/when that class is refactored out). - if (InferiorCallMmap(this, addr, 0, size, prot, - eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { - m_addr_to_mmap_size[addr] = size; - return Status(); - } else { - addr = LLDB_INVALID_ADDRESS; - return Status("unable to allocate %" PRIu64 - " bytes of memory with permissions %s", - size, GetPermissionsAsCString(permissions)); - } -#endif -} - -Status NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { - // FIXME see comments in AllocateMemory - required lower-level - // bits not in place yet (ThreadPlans) - return Status("not implemented"); -} - -lldb::addr_t NativeProcessLinux::GetSharedLibraryInfoAddress() { - // punt on this for now - return LLDB_INVALID_ADDRESS; -} - -size_t NativeProcessLinux::UpdateThreads() { - // The NativeProcessLinux monitoring threads are always up to date with - // respect to thread state and they keep the thread list populated properly. - // All this method needs to do is return the thread count. - return m_threads.size(); -} - -Status NativeProcessLinux::SetBreakpoint(lldb::addr_t addr, uint32_t size, - bool hardware) { - if (hardware) - return SetHardwareBreakpoint(addr, size); - else - return SetSoftwareBreakpoint(addr, size); -} - -Status NativeProcessLinux::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { - if (hardware) - return RemoveHardwareBreakpoint(addr); - else - return NativeProcessProtocol::RemoveBreakpoint(addr); -} - -llvm::Expected<llvm::ArrayRef<uint8_t>> -NativeProcessLinux::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { - // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the - // linux kernel does otherwise. - static const uint8_t g_arm_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; - static const uint8_t g_thumb_opcode[] = {0x01, 0xde}; - - switch (GetArchitecture().GetMachine()) { - case llvm::Triple::arm: - switch (size_hint) { - case 2: - return llvm::makeArrayRef(g_thumb_opcode); - case 4: - return llvm::makeArrayRef(g_arm_opcode); - default: - return llvm::createStringError(llvm::inconvertibleErrorCode(), - "Unrecognised trap opcode size hint!"); - } - default: - return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); - } -} - -Status NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t size, - size_t &bytes_read) { - if (ProcessVmReadvSupported()) { - // The process_vm_readv path is about 50 times faster than ptrace api. We - // want to use this syscall if it is supported. - - const ::pid_t pid = GetID(); - - struct iovec local_iov, remote_iov; - local_iov.iov_base = buf; - local_iov.iov_len = size; - remote_iov.iov_base = reinterpret_cast<void *>(addr); - remote_iov.iov_len = size; - - bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); - const bool success = bytes_read == size; - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - LLDB_LOG(log, - "using process_vm_readv to read {0} bytes from inferior " - "address {1:x}: {2}", - size, addr, success ? "Success" : llvm::sys::StrError(errno)); - - if (success) - return Status(); - // else the call failed for some reason, let's retry the read using ptrace - // api. - } - - unsigned char *dst = static_cast<unsigned char *>(buf); - size_t remainder; - long data; - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); - LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); - - for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { - Status error = NativeProcessLinux::PtraceWrapper( - PTRACE_PEEKDATA, GetID(), (void *)addr, nullptr, 0, &data); - if (error.Fail()) - return error; - - remainder = size - bytes_read; - remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; - - // Copy the data into our buffer - memcpy(dst, &data, remainder); - - LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); - addr += k_ptrace_word_size; - dst += k_ptrace_word_size; - } - return Status(); -} - -Status NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, - size_t size, size_t &bytes_written) { - const unsigned char *src = static_cast<const unsigned char *>(buf); - size_t remainder; - Status error; - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); - LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); - - for (bytes_written = 0; bytes_written < size; bytes_written += remainder) { - remainder = size - bytes_written; - remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; - - if (remainder == k_ptrace_word_size) { - unsigned long data = 0; - memcpy(&data, src, k_ptrace_word_size); - - LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); - error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), - (void *)addr, (void *)data); - if (error.Fail()) - return error; - } else { - unsigned char buff[8]; - size_t bytes_read; - error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read); - if (error.Fail()) - return error; - - memcpy(buff, src, remainder); - - size_t bytes_written_rec; - error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec); - if (error.Fail()) - return error; - - LLDB_LOG(log, "[{0:x}]:{1:x} ({2:x})", addr, *(const unsigned long *)src, - *(unsigned long *)buff); - } - - addr += k_ptrace_word_size; - src += k_ptrace_word_size; - } - return error; -} - -Status NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { - return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); -} - -Status NativeProcessLinux::GetEventMessage(lldb::tid_t tid, - unsigned long *message) { - return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message); -} - -Status NativeProcessLinux::Detach(lldb::tid_t tid) { - if (tid == LLDB_INVALID_THREAD_ID) - return Status(); - - return PtraceWrapper(PTRACE_DETACH, tid); -} - -bool NativeProcessLinux::HasThreadNoLock(lldb::tid_t thread_id) { - for (const auto &thread : m_threads) { - assert(thread && "thread list should not contain NULL threads"); - if (thread->GetID() == thread_id) { - // We have this thread. - return true; - } - } - - // We don't have this thread. - return false; -} - -bool NativeProcessLinux::StopTrackingThread(lldb::tid_t thread_id) { - Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - LLDB_LOG(log, "tid: {0})", thread_id); - - bool found = false; - for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { - if (*it && ((*it)->GetID() == thread_id)) { - m_threads.erase(it); - found = true; - break; - } - } - - if (found) - StopTracingForThread(thread_id); - SignalIfAllThreadsStopped(); - return found; -} - -NativeThreadLinux &NativeProcessLinux::AddThread(lldb::tid_t thread_id) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); - LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); - - assert(!HasThreadNoLock(thread_id) && - "attempted to add a thread by id that already exists"); - - // If this is the first thread, save it as the current thread - if (m_threads.empty()) - SetCurrentThreadID(thread_id); - - m_threads.push_back(llvm::make_unique<NativeThreadLinux>(*this, thread_id)); - - if (m_pt_proces_trace_id != LLDB_INVALID_UID) { - auto traceMonitor = ProcessorTraceMonitor::Create( - GetID(), thread_id, m_pt_process_trace_config, true); - if (traceMonitor) { - m_pt_traced_thread_group.insert(thread_id); - m_processor_trace_monitor.insert( - std::make_pair(thread_id, std::move(*traceMonitor))); - } else { - LLDB_LOG(log, "failed to start trace on thread {0}", thread_id); - Status error(traceMonitor.takeError()); - LLDB_LOG(log, "error {0}", error); - } - } - - return static_cast<NativeThreadLinux &>(*m_threads.back()); -} - -Status NativeProcessLinux::GetLoadedModuleFileSpec(const char *module_path, - FileSpec &file_spec) { - Status error = PopulateMemoryRegionCache(); - if (error.Fail()) - return error; - - FileSpec module_file_spec(module_path); - FileSystem::Instance().Resolve(module_file_spec); - - file_spec.Clear(); - for (const auto &it : m_mem_region_cache) { - if (it.second.GetFilename() == module_file_spec.GetFilename()) { - file_spec = it.second; - return Status(); - } - } - return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", - module_file_spec.GetFilename().AsCString(), GetID()); -} - -Status NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef &file_name, - lldb::addr_t &load_addr) { - load_addr = LLDB_INVALID_ADDRESS; - Status error = PopulateMemoryRegionCache(); - if (error.Fail()) - return error; - - FileSpec file(file_name); - for (const auto &it : m_mem_region_cache) { - if (it.second == file) { - load_addr = it.first.GetRange().GetRangeBase(); - return Status(); - } - } - return Status("No load address found for specified file."); -} - -NativeThreadLinux *NativeProcessLinux::GetThreadByID(lldb::tid_t tid) { - return static_cast<NativeThreadLinux *>( - NativeProcessProtocol::GetThreadByID(tid)); -} - -Status NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, - lldb::StateType state, int signo) { - Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - LLDB_LOG(log, "tid: {0}", thread.GetID()); - - // Before we do the resume below, first check if we have a pending stop - // notification that is currently waiting for all threads to stop. This is - // potentially a buggy situation since we're ostensibly waiting for threads - // to stop before we send out the pending notification, and here we are - // resuming one before we send out the pending stop notification. - if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { - LLDB_LOG(log, - "about to resume tid {0} per explicit request but we have a " - "pending stop notification (tid {1}) that is actively " - "waiting for this thread to stop. Valid sequence of events?", - thread.GetID(), m_pending_notification_tid); - } - - // Request a resume. We expect this to be synchronous and the system to - // reflect it is running after this completes. - switch (state) { - case eStateRunning: { - const auto resume_result = thread.Resume(signo); - if (resume_result.Success()) - SetState(eStateRunning, true); - return resume_result; - } - case eStateStepping: { - const auto step_result = thread.SingleStep(signo); - if (step_result.Success()) - SetState(eStateRunning, true); - return step_result; - } - default: - LLDB_LOG(log, "Unhandled state {0}.", state); - llvm_unreachable("Unhandled state for resume"); - } -} - -//===----------------------------------------------------------------------===// - -void NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid) { - Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - LLDB_LOG(log, "about to process event: (triggering_tid: {0})", - triggering_tid); - - m_pending_notification_tid = triggering_tid; - - // Request a stop for all the thread stops that need to be stopped and are - // not already known to be stopped. - for (const auto &thread : m_threads) { - if (StateIsRunningState(thread->GetState())) - static_cast<NativeThreadLinux *>(thread.get())->RequestStop(); - } - - SignalIfAllThreadsStopped(); - LLDB_LOG(log, "event processing done"); -} - -void NativeProcessLinux::SignalIfAllThreadsStopped() { - if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) - return; // No pending notification. Nothing to do. - - for (const auto &thread_sp : m_threads) { - if (StateIsRunningState(thread_sp->GetState())) - return; // Some threads are still running. Don't signal yet. - } - - // We have a pending notification and all threads have stopped. - Log *log( - GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); - - // Clear any temporary breakpoints we used to implement software single - // stepping. - for (const auto &thread_info : m_threads_stepping_with_breakpoint) { - Status error = RemoveBreakpoint(thread_info.second); - if (error.Fail()) - LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", - thread_info.first, error); - } - m_threads_stepping_with_breakpoint.clear(); - - // Notify the delegate about the stop - SetCurrentThreadID(m_pending_notification_tid); - SetState(StateType::eStateStopped, true); - m_pending_notification_tid = LLDB_INVALID_THREAD_ID; -} - -void NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread) { - Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - LLDB_LOG(log, "tid: {0}", thread.GetID()); - - if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && - StateIsRunningState(thread.GetState())) { - // We will need to wait for this new thread to stop as well before firing - // the notification. - thread.RequestStop(); - } -} - -void NativeProcessLinux::SigchldHandler() { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - // Process all pending waitpid notifications. - while (true) { - int status = -1; - ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, ::waitpid, -1, &status, - __WALL | __WNOTHREAD | WNOHANG); - - if (wait_pid == 0) - break; // We are done. - - if (wait_pid == -1) { - Status error(errno, eErrorTypePOSIX); - LLDB_LOG(log, "waitpid (-1, &status, _) failed: {0}", error); - break; - } - - WaitStatus wait_status = WaitStatus::Decode(status); - bool exited = wait_status.type == WaitStatus::Exit || - (wait_status.type == WaitStatus::Signal && - wait_pid == static_cast<::pid_t>(GetID())); - - LLDB_LOG( - log, - "waitpid (-1, &status, _) => pid = {0}, status = {1}, exited = {2}", - wait_pid, wait_status, exited); - - MonitorCallback(wait_pid, exited, wait_status); - } -} - -// Wrapper for ptrace to catch errors and log calls. Note that ptrace sets -// errno on error because -1 can be a valid result (i.e. for PTRACE_PEEK*) -Status NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, - void *data, size_t data_size, - long *result) { - Status error; - long int ret; - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - - PtraceDisplayBytes(req, data, data_size); - - errno = 0; - if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) - ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid), - *(unsigned int *)addr, data); - else - ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid), - addr, data); - - if (ret == -1) - error.SetErrorToErrno(); - - if (result) - *result = ret; - - LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data, - data_size, ret); - - PtraceDisplayBytes(req, data, data_size); - - if (error.Fail()) - LLDB_LOG(log, "ptrace() failed: {0}", error); - - return error; -} - -llvm::Expected<ProcessorTraceMonitor &> -NativeProcessLinux::LookupProcessorTraceInstance(lldb::user_id_t traceid, - lldb::tid_t thread) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - if (thread == LLDB_INVALID_THREAD_ID && traceid == m_pt_proces_trace_id) { - LLDB_LOG(log, "thread not specified: {0}", traceid); - return Status("tracing not active thread not specified").ToError(); - } - - for (auto& iter : m_processor_trace_monitor) { - if (traceid == iter.second->GetTraceID() && - (thread == iter.first || thread == LLDB_INVALID_THREAD_ID)) - return *(iter.second); - } - - LLDB_LOG(log, "traceid not being traced: {0}", traceid); - return Status("tracing not active for this thread").ToError(); -} - -Status NativeProcessLinux::GetMetaData(lldb::user_id_t traceid, - lldb::tid_t thread, - llvm::MutableArrayRef<uint8_t> &buffer, - size_t offset) { - TraceOptions trace_options; - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - Status error; - - LLDB_LOG(log, "traceid {0}", traceid); - - auto perf_monitor = LookupProcessorTraceInstance(traceid, thread); - if (!perf_monitor) { - LLDB_LOG(log, "traceid not being traced: {0}", traceid); - buffer = buffer.slice(buffer.size()); - error = perf_monitor.takeError(); - return error; - } - return (*perf_monitor).ReadPerfTraceData(buffer, offset); -} - -Status NativeProcessLinux::GetData(lldb::user_id_t traceid, lldb::tid_t thread, - llvm::MutableArrayRef<uint8_t> &buffer, - size_t offset) { - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - Status error; - - LLDB_LOG(log, "traceid {0}", traceid); - - auto perf_monitor = LookupProcessorTraceInstance(traceid, thread); - if (!perf_monitor) { - LLDB_LOG(log, "traceid not being traced: {0}", traceid); - buffer = buffer.slice(buffer.size()); - error = perf_monitor.takeError(); - return error; - } - return (*perf_monitor).ReadPerfTraceAux(buffer, offset); -} - -Status NativeProcessLinux::GetTraceConfig(lldb::user_id_t traceid, - TraceOptions &config) { - Status error; - if (config.getThreadID() == LLDB_INVALID_THREAD_ID && - m_pt_proces_trace_id == traceid) { - if (m_pt_proces_trace_id == LLDB_INVALID_UID) { - error.SetErrorString("tracing not active for this process"); - return error; - } - config = m_pt_process_trace_config; - } else { - auto perf_monitor = - LookupProcessorTraceInstance(traceid, config.getThreadID()); - if (!perf_monitor) { - error = perf_monitor.takeError(); - return error; - } - error = (*perf_monitor).GetTraceConfig(config); - } - return error; -} - -lldb::user_id_t -NativeProcessLinux::StartTraceGroup(const TraceOptions &config, - Status &error) { - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - if (config.getType() != TraceType::eTraceTypeProcessorTrace) - return LLDB_INVALID_UID; - - if (m_pt_proces_trace_id != LLDB_INVALID_UID) { - error.SetErrorString("tracing already active on this process"); - return m_pt_proces_trace_id; - } - - for (const auto &thread_sp : m_threads) { - if (auto traceInstance = ProcessorTraceMonitor::Create( - GetID(), thread_sp->GetID(), config, true)) { - m_pt_traced_thread_group.insert(thread_sp->GetID()); - m_processor_trace_monitor.insert( - std::make_pair(thread_sp->GetID(), std::move(*traceInstance))); - } - } - - m_pt_process_trace_config = config; - error = ProcessorTraceMonitor::GetCPUType(m_pt_process_trace_config); - - // Trace on Complete process will have traceid of 0 - m_pt_proces_trace_id = 0; - - LLDB_LOG(log, "Process Trace ID {0}", m_pt_proces_trace_id); - return m_pt_proces_trace_id; -} - -lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config, - Status &error) { - if (config.getType() != TraceType::eTraceTypeProcessorTrace) - return NativeProcessProtocol::StartTrace(config, error); - - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - - lldb::tid_t threadid = config.getThreadID(); - - if (threadid == LLDB_INVALID_THREAD_ID) - return StartTraceGroup(config, error); - - auto thread_sp = GetThreadByID(threadid); - if (!thread_sp) { - // Thread not tracked by lldb so don't trace. - error.SetErrorString("invalid thread id"); - return LLDB_INVALID_UID; - } - - const auto &iter = m_processor_trace_monitor.find(threadid); - if (iter != m_processor_trace_monitor.end()) { - LLDB_LOG(log, "Thread already being traced"); - error.SetErrorString("tracing already active on this thread"); - return LLDB_INVALID_UID; - } - - auto traceMonitor = - ProcessorTraceMonitor::Create(GetID(), threadid, config, false); - if (!traceMonitor) { - error = traceMonitor.takeError(); - LLDB_LOG(log, "error {0}", error); - return LLDB_INVALID_UID; - } - lldb::user_id_t ret_trace_id = (*traceMonitor)->GetTraceID(); - m_processor_trace_monitor.insert( - std::make_pair(threadid, std::move(*traceMonitor))); - return ret_trace_id; -} - -Status NativeProcessLinux::StopTracingForThread(lldb::tid_t thread) { - Status error; - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - LLDB_LOG(log, "Thread {0}", thread); - - const auto& iter = m_processor_trace_monitor.find(thread); - if (iter == m_processor_trace_monitor.end()) { - error.SetErrorString("tracing not active for this thread"); - return error; - } - - if (iter->second->GetTraceID() == m_pt_proces_trace_id) { - // traceid maps to the whole process so we have to erase it from the thread - // group. - LLDB_LOG(log, "traceid maps to process"); - m_pt_traced_thread_group.erase(thread); - } - m_processor_trace_monitor.erase(iter); - - return error; -} - -Status NativeProcessLinux::StopTrace(lldb::user_id_t traceid, - lldb::tid_t thread) { - Status error; - - TraceOptions trace_options; - trace_options.setThreadID(thread); - error = NativeProcessLinux::GetTraceConfig(traceid, trace_options); - - if (error.Fail()) - return error; - - switch (trace_options.getType()) { - case lldb::TraceType::eTraceTypeProcessorTrace: - if (traceid == m_pt_proces_trace_id && - thread == LLDB_INVALID_THREAD_ID) - StopProcessorTracingOnProcess(); - else - error = StopProcessorTracingOnThread(traceid, thread); - break; - default: - error.SetErrorString("trace not supported"); - break; - } - - return error; -} - -void NativeProcessLinux::StopProcessorTracingOnProcess() { - for (auto thread_id_iter : m_pt_traced_thread_group) - m_processor_trace_monitor.erase(thread_id_iter); - m_pt_traced_thread_group.clear(); - m_pt_proces_trace_id = LLDB_INVALID_UID; -} - -Status NativeProcessLinux::StopProcessorTracingOnThread(lldb::user_id_t traceid, - lldb::tid_t thread) { - Status error; - Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); - - if (thread == LLDB_INVALID_THREAD_ID) { - for (auto& iter : m_processor_trace_monitor) { - if (iter.second->GetTraceID() == traceid) { - // Stopping a trace instance for an individual thread hence there will - // only be one traceid that can match. - m_processor_trace_monitor.erase(iter.first); - return error; - } - LLDB_LOG(log, "Trace ID {0}", iter.second->GetTraceID()); - } - - LLDB_LOG(log, "Invalid TraceID"); - error.SetErrorString("invalid trace id"); - return error; - } - - // thread is specified so we can use find function on the map. - const auto& iter = m_processor_trace_monitor.find(thread); - if (iter == m_processor_trace_monitor.end()) { - // thread not found in our map. - LLDB_LOG(log, "thread not being traced"); - error.SetErrorString("tracing not active for this thread"); - return error; - } - if (iter->second->GetTraceID() != traceid) { - // traceid did not match so it has to be invalid. - LLDB_LOG(log, "Invalid TraceID"); - error.SetErrorString("invalid trace id"); - return error; - } - - LLDB_LOG(log, "UID - {0} , Thread -{1}", traceid, thread); - - if (traceid == m_pt_proces_trace_id) { - // traceid maps to the whole process so we have to erase it from the thread - // group. - LLDB_LOG(log, "traceid maps to process"); - m_pt_traced_thread_group.erase(thread); - } - m_processor_trace_monitor.erase(iter); - - return error; -} |