summaryrefslogtreecommitdiff
path: root/source/Plugins/Process/Linux/NativeProcessLinux.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/Process/Linux/NativeProcessLinux.cpp')
-rw-r--r--source/Plugins/Process/Linux/NativeProcessLinux.cpp4619
1 files changed, 2195 insertions, 2424 deletions
diff --git a/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 00f4010742b00..94b9efc2b2b01 100644
--- a/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -11,8 +11,8 @@
// C Includes
#include <errno.h>
-#include <string.h>
#include <stdint.h>
+#include <string.h>
#include <unistd.h>
// C++ Includes
@@ -29,9 +29,11 @@
#include "lldb/Core/RegisterValue.h"
#include "lldb/Core/State.h"
#include "lldb/Host/Host.h"
+#include "lldb/Host/HostProcess.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Host/common/NativeBreakpoint.h"
#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/linux/ProcessLauncherLinux.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/ProcessLaunchInfo.h"
@@ -40,12 +42,13 @@
#include "lldb/Utility/PseudoTerminal.h"
#include "lldb/Utility/StringExtractor.h"
-#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "NativeThreadLinux.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "ProcFileReader.h"
#include "Procfs.h"
-// System includes - They have to be included after framework includes because they define some
+// System includes - They have to be included after framework includes because
+// they define some
// macros which collide with variable names in other modules
#include <linux/unistd.h>
#include <sys/socket.h>
@@ -55,15 +58,12 @@
#include <sys/user.h>
#include <sys/wait.h>
-#include "lldb/Host/linux/Personality.h"
#include "lldb/Host/linux/Ptrace.h"
#include "lldb/Host/linux/Uio.h"
-#define LLDB_PERSONALITY_GET_CURRENT_SETTINGS 0xffffffff
-
// Support hardware breakpoints in case it has not been defined
#ifndef TRAP_HWBKPT
- #define TRAP_HWBKPT 4
+#define TRAP_HWBKPT 4
#endif
using namespace lldb;
@@ -73,2113 +73,1911 @@ using namespace llvm;
// Private bits we only need internally.
-static bool ProcessVmReadvSupported()
-{
- static bool is_supported;
- static std::once_flag flag;
-
- std::call_once(flag, [] {
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_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 (log)
- {
- if (is_supported)
- log->Printf("%s: Detected kernel support for process_vm_readv syscall. Fast memory reads enabled.",
- __FUNCTION__);
- else
- log->Printf("%s: syscall process_vm_readv failed (error: %s). Fast memory reads disabled.",
- __FUNCTION__, strerror(errno));
- }
- });
+static bool ProcessVmReadvSupported() {
+ static bool is_supported;
+ static std::once_flag flag;
- return is_supported;
-}
-
-namespace
-{
-Error
-ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch)
-{
- // Grab process info for the running process.
- ProcessInstanceInfo process_info;
- if (!Host::GetProcessInfo(pid, process_info))
- return Error("failed to get process info");
-
- // Resolve the executable module.
- ModuleSpecList module_specs;
- if (!ObjectFile::GetModuleSpecifications(process_info.GetExecutableFile(), 0, 0, module_specs))
- return Error("failed to get module specifications");
- assert(module_specs.GetSize() == 1);
-
- arch = module_specs.GetModuleSpecRefAtIndex(0).GetArchitecture();
- if (arch.IsValid())
- return Error();
- else
- return Error("failed to retrieve a valid architecture from the exe module");
-}
-
-// Used to notify the parent about which part of the launch sequence failed.
-enum LaunchCallSpecifier
-{
- ePtraceFailed,
- eDupStdinFailed,
- eDupStdoutFailed,
- eDupStderrFailed,
- eChdirFailed,
- eExecFailed,
- eSetGidFailed,
- eSetSigMaskFailed,
- eLaunchCallMax = eSetSigMaskFailed
-};
-
-static uint8_t LLVM_ATTRIBUTE_NORETURN
-ExitChildAbnormally(LaunchCallSpecifier spec)
-{
- static_assert(eLaunchCallMax < 0x8, "Have more launch calls than we are able to represent");
- // This may truncate the topmost bits of the errno because the exit code is only 8 bits wide.
- // However, it should still give us a pretty good indication of what went wrong. (And the
- // most common errors have small numbers anyway).
- _exit(unsigned(spec) | (errno << 3));
-}
-
-// The second member is the errno (or its 5 lowermost bits anyway).
-inline std::pair<LaunchCallSpecifier, uint8_t>
-DecodeChildExitCode(int exit_code)
-{
- return std::make_pair(LaunchCallSpecifier(exit_code & 0x7), exit_code >> 3);
-}
-
-void
-MaybeLogLaunchInfo(const ProcessLaunchInfo &info)
-{
+ std::call_once(flag, [] {
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
- if (!log)
- return;
- if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO))
- log->Printf("%s: setting STDIN to '%s'", __FUNCTION__, action->GetFileSpec().GetCString());
- else
- log->Printf("%s leaving STDIN as is", __FUNCTION__);
-
- if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO))
- log->Printf("%s setting STDOUT to '%s'", __FUNCTION__, action->GetFileSpec().GetCString());
- else
- log->Printf("%s leaving STDOUT as is", __FUNCTION__);
-
- if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO))
- log->Printf("%s setting STDERR to '%s'", __FUNCTION__, action->GetFileSpec().GetCString());
- else
- log->Printf("%s leaving STDERR as is", __FUNCTION__);
+ uint32_t source = 0x47424742;
+ uint32_t dest = 0;
- int i = 0;
- for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; ++args, ++i)
- log->Printf("%s arg %d: \"%s\"", __FUNCTION__, i, *args ? *args : "nullptr");
-}
+ struct iovec local, remote;
+ remote.iov_base = &source;
+ local.iov_base = &dest;
+ remote.iov_len = local.iov_len = sizeof source;
-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++;
+ // 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 (log) {
+ if (is_supported)
+ log->Printf("%s: Detected kernel support for process_vm_readv syscall. "
+ "Fast memory reads enabled.",
+ __FUNCTION__);
+ else
+ log->Printf("%s: syscall process_vm_readv failed (error: %s). Fast "
+ "memory reads disabled.",
+ __FUNCTION__, strerror(errno));
}
-}
+ });
- void
- PtraceDisplayBytes(int &req, void *data, size_t data_size)
- {
- StreamString buf;
- Log *verbose_log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (
- POSIX_LOG_PTRACE | POSIX_LOG_VERBOSE));
-
- if (verbose_log)
- {
- switch(req)
- {
- case PTRACE_POKETEXT:
- {
- DisplayBytes(buf, &data, 8);
- verbose_log->Printf("PTRACE_POKETEXT %s", buf.GetData());
- break;
- }
- case PTRACE_POKEDATA:
- {
- DisplayBytes(buf, &data, 8);
- verbose_log->Printf("PTRACE_POKEDATA %s", buf.GetData());
- break;
- }
- case PTRACE_POKEUSER:
- {
- DisplayBytes(buf, &data, 8);
- verbose_log->Printf("PTRACE_POKEUSER %s", buf.GetData());
- break;
- }
- case PTRACE_SETREGS:
- {
- DisplayBytes(buf, data, data_size);
- verbose_log->Printf("PTRACE_SETREGS %s", buf.GetData());
- break;
- }
- case PTRACE_SETFPREGS:
- {
- DisplayBytes(buf, data, data_size);
- verbose_log->Printf("PTRACE_SETFPREGS %s", buf.GetData());
- break;
- }
- case PTRACE_SETSIGINFO:
- {
- DisplayBytes(buf, data, sizeof(siginfo_t));
- verbose_log->Printf("PTRACE_SETSIGINFO %s", 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);
- verbose_log->Printf("PTRACE_SETREGSET %s", buf.GetData());
- break;
- }
- default:
- {
- }
- }
- }
- }
+ return is_supported;
+}
- 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");
+namespace {
+void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (!log)
+ return;
+
+ if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO))
+ log->Printf("%s: setting STDIN to '%s'", __FUNCTION__,
+ action->GetFileSpec().GetCString());
+ else
+ log->Printf("%s leaving STDIN as is", __FUNCTION__);
+
+ if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO))
+ log->Printf("%s setting STDOUT to '%s'", __FUNCTION__,
+ action->GetFileSpec().GetCString());
+ else
+ log->Printf("%s leaving STDOUT as is", __FUNCTION__);
+
+ if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO))
+ log->Printf("%s setting STDERR to '%s'", __FUNCTION__,
+ action->GetFileSpec().GetCString());
+ else
+ log->Printf("%s leaving STDERR as is", __FUNCTION__);
+
+ int i = 0;
+ for (const char **args = info.GetArguments().GetConstArgumentVector(); *args;
+ ++args, ++i)
+ log->Printf("%s arg %d: \"%s\"", __FUNCTION__, i,
+ *args ? *args : "nullptr");
+}
+
+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) {
+ StreamString buf;
+ Log *verbose_log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(
+ POSIX_LOG_PTRACE | POSIX_LOG_VERBOSE));
+
+ if (verbose_log) {
+ switch (req) {
+ case PTRACE_POKETEXT: {
+ DisplayBytes(buf, &data, 8);
+ verbose_log->Printf("PTRACE_POKETEXT %s", buf.GetData());
+ break;
+ }
+ case PTRACE_POKEDATA: {
+ DisplayBytes(buf, &data, 8);
+ verbose_log->Printf("PTRACE_POKEDATA %s", buf.GetData());
+ break;
+ }
+ case PTRACE_POKEUSER: {
+ DisplayBytes(buf, &data, 8);
+ verbose_log->Printf("PTRACE_POKEUSER %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETREGS: {
+ DisplayBytes(buf, data, data_size);
+ verbose_log->Printf("PTRACE_SETREGS %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETFPREGS: {
+ DisplayBytes(buf, data, data_size);
+ verbose_log->Printf("PTRACE_SETFPREGS %s", buf.GetData());
+ break;
+ }
+ case PTRACE_SETSIGINFO: {
+ DisplayBytes(buf, data, sizeof(siginfo_t));
+ verbose_log->Printf("PTRACE_SETSIGINFO %s", 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);
+ verbose_log->Printf("PTRACE_SETREGSET %s", 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 Error
-EnsureFDFlags(int fd, int flags)
-{
- Error error;
+static Error EnsureFDFlags(int fd, int flags) {
+ Error 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;
- }
+ 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
// -----------------------------------------------------------------------------
-Error
-NativeProcessProtocol::Launch (
+Error NativeProcessProtocol::Launch(
ProcessLaunchInfo &launch_info,
- NativeProcessProtocol::NativeDelegate &native_delegate,
- MainLoop &mainloop,
- NativeProcessProtocolSP &native_process_sp)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
-
- Error error;
-
- // Verify the working directory is valid if one was specified.
- 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;
- }
+ NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop,
+ NativeProcessProtocolSP &native_process_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ Error error;
+
+ // Verify the working directory is valid if one was specified.
+ 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;
+ }
- // Create the NativeProcessLinux in launch mode.
- native_process_sp.reset (new NativeProcessLinux ());
+ // Create the NativeProcessLinux in launch mode.
+ native_process_sp.reset(new NativeProcessLinux());
- if (!native_process_sp->RegisterNativeDelegate (native_delegate))
- {
- native_process_sp.reset ();
- error.SetErrorStringWithFormat ("failed to register the native delegate");
- return error;
- }
+ if (!native_process_sp->RegisterNativeDelegate(native_delegate)) {
+ native_process_sp.reset();
+ error.SetErrorStringWithFormat("failed to register the native delegate");
+ return error;
+ }
- error = std::static_pointer_cast<NativeProcessLinux>(native_process_sp)->LaunchInferior(mainloop, launch_info);
+ error = std::static_pointer_cast<NativeProcessLinux>(native_process_sp)
+ ->LaunchInferior(mainloop, launch_info);
- if (error.Fail ())
- {
- native_process_sp.reset ();
- if (log)
- log->Printf ("NativeProcessLinux::%s failed to launch process: %s", __FUNCTION__, error.AsCString ());
- return error;
- }
+ if (error.Fail()) {
+ native_process_sp.reset();
+ if (log)
+ log->Printf("NativeProcessLinux::%s failed to launch process: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ }
- launch_info.SetProcessID (native_process_sp->GetID ());
+ launch_info.SetProcessID(native_process_sp->GetID());
- return error;
+ return error;
}
-Error
-NativeProcessProtocol::Attach (
- lldb::pid_t pid,
- NativeProcessProtocol::NativeDelegate &native_delegate,
- MainLoop &mainloop,
- NativeProcessProtocolSP &native_process_sp)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
- if (log && log->GetMask ().Test (POSIX_LOG_VERBOSE))
- log->Printf ("NativeProcessLinux::%s(pid = %" PRIi64 ")", __FUNCTION__, pid);
-
- // Retrieve the architecture for the running process.
- ArchSpec process_arch;
- Error error = ResolveProcessArchitecture(pid, process_arch);
- if (!error.Success ())
- return error;
+Error NativeProcessProtocol::Attach(
+ lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
+ MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log && log->GetMask().Test(POSIX_LOG_VERBOSE))
+ log->Printf("NativeProcessLinux::%s(pid = %" PRIi64 ")", __FUNCTION__, pid);
- std::shared_ptr<NativeProcessLinux> native_process_linux_sp (new NativeProcessLinux ());
+ // Retrieve the architecture for the running process.
+ ArchSpec process_arch;
+ Error error = ResolveProcessArchitecture(pid, process_arch);
+ if (!error.Success())
+ return error;
- if (!native_process_linux_sp->RegisterNativeDelegate (native_delegate))
- {
- error.SetErrorStringWithFormat ("failed to register the native delegate");
- return error;
- }
+ std::shared_ptr<NativeProcessLinux> native_process_linux_sp(
+ new NativeProcessLinux());
- native_process_linux_sp->AttachToInferior (mainloop, pid, error);
- if (!error.Success ())
- return error;
+ if (!native_process_linux_sp->RegisterNativeDelegate(native_delegate)) {
+ error.SetErrorStringWithFormat("failed to register the native delegate");
+ return error;
+ }
- native_process_sp = native_process_linux_sp;
+ native_process_linux_sp->AttachToInferior(mainloop, pid, error);
+ if (!error.Success())
return error;
+
+ native_process_sp = native_process_linux_sp;
+ return error;
}
// -----------------------------------------------------------------------------
// Public Instance Methods
// -----------------------------------------------------------------------------
-NativeProcessLinux::NativeProcessLinux () :
- NativeProcessProtocol (LLDB_INVALID_PROCESS_ID),
- m_arch (),
- m_supports_mem_region (eLazyBoolCalculate),
- m_mem_region_cache (),
- m_pending_notification_tid(LLDB_INVALID_THREAD_ID)
-{
-}
-
-void
-NativeProcessLinux::AttachToInferior (MainLoop &mainloop, lldb::pid_t pid, Error &error)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
- if (log)
- log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__, pid);
-
- m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD,
- [this] (MainLoopBase &) { SigchldHandler(); }, error);
- if (! m_sigchld_handle)
- return;
-
- error = ResolveProcessArchitecture(pid, m_arch);
- if (!error.Success())
- return;
-
- // Set the architecture to the exe architecture.
- if (log)
- log->Printf ("NativeProcessLinux::%s (pid = %" PRIi64 ") detected architecture %s", __FUNCTION__, pid, m_arch.GetArchitectureName ());
-
- m_pid = pid;
- SetState(eStateAttaching);
-
- Attach(pid, error);
-}
-
-void
-NativeProcessLinux::ChildFunc(const ProcessLaunchInfo &info)
-{
- // Start tracing this child that is about to exec.
- if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1)
- ExitChildAbnormally(ePtraceFailed);
-
- // Do not inherit setgid powers.
- if (setgid(getgid()) != 0)
- ExitChildAbnormally(eSetGidFailed);
-
- // Attempt to have our own process group.
- if (setpgid(0, 0) != 0)
- {
- // FIXME log that this failed. This is common.
- // Don't allow this to prevent an inferior exec.
- }
-
- // Dup file descriptors if needed.
- if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO))
- if (!DupDescriptor(action->GetFileSpec(), STDIN_FILENO, O_RDONLY))
- ExitChildAbnormally(eDupStdinFailed);
-
- if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO))
- if (!DupDescriptor(action->GetFileSpec(), STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
- ExitChildAbnormally(eDupStdoutFailed);
-
- if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO))
- if (!DupDescriptor(action->GetFileSpec(), STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
- ExitChildAbnormally(eDupStderrFailed);
+NativeProcessLinux::NativeProcessLinux()
+ : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID), m_arch(),
+ m_supports_mem_region(eLazyBoolCalculate), m_mem_region_cache(),
+ m_pending_notification_tid(LLDB_INVALID_THREAD_ID) {}
+
+void NativeProcessLinux::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid,
+ Error &error) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s (pid = %" PRIi64 ")", __FUNCTION__,
+ pid);
+
+ m_sigchld_handle = mainloop.RegisterSignal(
+ SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error);
+ if (!m_sigchld_handle)
+ return;
+
+ error = ResolveProcessArchitecture(pid, m_arch);
+ if (!error.Success())
+ return;
+
+ // Set the architecture to the exe architecture.
+ if (log)
+ log->Printf("NativeProcessLinux::%s (pid = %" PRIi64
+ ") detected architecture %s",
+ __FUNCTION__, pid, m_arch.GetArchitectureName());
+
+ m_pid = pid;
+ SetState(eStateAttaching);
+
+ Attach(pid, error);
+}
+
+Error NativeProcessLinux::LaunchInferior(MainLoop &mainloop,
+ ProcessLaunchInfo &launch_info) {
+ Error error;
+ m_sigchld_handle = mainloop.RegisterSignal(
+ SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error);
+ if (!m_sigchld_handle)
+ return error;
- // Close everything besides stdin, stdout, and stderr that has no file
- // action to avoid leaking
- for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd)
- if (!info.GetFileActionForFD(fd))
- close(fd);
+ SetState(eStateLaunching);
- // Change working directory
- if (info.GetWorkingDirectory() && 0 != ::chdir(info.GetWorkingDirectory().GetCString()))
- ExitChildAbnormally(eChdirFailed);
+ MaybeLogLaunchInfo(launch_info);
- // Disable ASLR if requested.
- if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR))
- {
- const int old_personality = personality(LLDB_PERSONALITY_GET_CURRENT_SETTINGS);
- if (old_personality == -1)
- {
- // Can't retrieve Linux personality. Cannot disable ASLR.
- }
- else
- {
- const int new_personality = personality(ADDR_NO_RANDOMIZE | old_personality);
- if (new_personality == -1)
- {
- // Disabling ASLR failed.
- }
- else
- {
- // Disabling ASLR succeeded.
- }
- }
- }
-
- // Clear the signal mask to prevent the child from being affected by
- // any masking done by the parent.
- sigset_t set;
- if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0)
- ExitChildAbnormally(eSetSigMaskFailed);
+ ::pid_t pid =
+ ProcessLauncherLinux().LaunchProcess(launch_info, error).GetProcessId();
+ if (error.Fail())
+ return error;
- const char **argv = info.GetArguments().GetConstArgumentVector();
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
- // Propagate the environment if one is not supplied.
- const char **envp = info.GetEnvironmentEntries().GetConstArgumentVector();
- if (envp == NULL || envp[0] == NULL)
- envp = const_cast<const char **>(environ);
+ // Wait for the child process to trap on its call to execve.
+ ::pid_t wpid;
+ int status;
+ if ((wpid = waitpid(pid, &status, 0)) < 0) {
+ error.SetErrorToErrno();
+ if (log)
+ log->Printf("NativeProcessLinux::%s waitpid for inferior failed with %s",
+ __FUNCTION__, error.AsCString());
- // Execute. We should never return...
- execve(argv[0], const_cast<char *const *>(argv), const_cast<char *const *>(envp));
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now,
+ // using eStateInvalid.
+ SetState(StateType::eStateInvalid);
- if (errno == ETXTBSY)
- {
- // On android M and earlier we can get this error because the adb deamon can hold a write
- // handle on the executable even after it has finished uploading it. This state lasts
- // only a short time and happens only when there are many concurrent adb commands being
- // issued, such as when running the test suite. (The file remains open when someone does
- // an "adb shell" command in the fork() child before it has had a chance to exec.) Since
- // this state should clear up quickly, wait a while and then give it one more go.
- usleep(50000);
- execve(argv[0], const_cast<char *const *>(argv), const_cast<char *const *>(envp));
- }
-
- // ...unless exec fails. In which case we definitely need to end the child here.
- ExitChildAbnormally(eExecFailed);
-}
+ return error;
+ }
+ assert(WIFSTOPPED(status) && (wpid == static_cast<::pid_t>(pid)) &&
+ "Could not sync with inferior process.");
-Error
-NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info)
-{
- Error error;
- m_sigchld_handle = mainloop.RegisterSignal(SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error);
- if (!m_sigchld_handle)
- return error;
+ if (log)
+ log->Printf("NativeProcessLinux::%s inferior started, now in stopped state",
+ __FUNCTION__);
- SetState(eStateLaunching);
+ error = SetDefaultPtraceOpts(pid);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s inferior failed to set default "
+ "ptrace options: %s",
+ __FUNCTION__, error.AsCString());
- lldb_utility::PseudoTerminal terminal;
- const size_t err_len = 1024;
- char err_str[err_len];
- lldb::pid_t pid;
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For now,
+ // using eStateInvalid.
+ SetState(StateType::eStateInvalid);
- MaybeLogLaunchInfo(launch_info);
+ return error;
+ }
- if ((pid = terminal.Fork(err_str, err_len)) == static_cast<lldb::pid_t> (-1))
- {
- error.SetErrorToGenericError();
- error.SetErrorStringWithFormat("Process fork failed: %s", err_str);
- return error;
- }
+ // Release the master terminal descriptor and pass it off to the
+ // NativeProcessLinux instance. Similarly stash the inferior pid.
+ m_terminal_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ m_pid = pid;
+ launch_info.SetProcessID(pid);
- // Child process.
- if (pid == 0)
- {
- // First, make sure we disable all logging. If we are logging to stdout, our logs can be
- // mistaken for inferior output.
- Log::DisableAllLogChannels(nullptr);
+ if (m_terminal_fd != -1) {
+ error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s inferior EnsureFDFlags failed for "
+ "ensuring terminal O_NONBLOCK setting: %s",
+ __FUNCTION__, error.AsCString());
- // terminal has already dupped the tty descriptors to stdin/out/err.
- // This closes original fd from which they were copied (and avoids
- // leaking descriptors to the debugged process.
- terminal.CloseSlaveFileDescriptor();
+ // Mark the inferior as invalid.
+ // FIXME this could really use a new state - eStateLaunchFailure. For
+ // now, using eStateInvalid.
+ SetState(StateType::eStateInvalid);
- ChildFunc(launch_info);
+ return error;
}
+ }
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__,
+ uint64_t(pid));
- // Wait for the child process to trap on its call to execve.
- ::pid_t wpid;
- int status;
- if ((wpid = waitpid(pid, &status, 0)) < 0)
- {
- error.SetErrorToErrno();
- if (log)
- log->Printf ("NativeProcessLinux::%s waitpid for inferior failed with %s",
- __FUNCTION__, error.AsCString ());
+ ResolveProcessArchitecture(m_pid, m_arch);
+ NativeThreadLinuxSP thread_sp = AddThread(pid);
+ assert(thread_sp && "AddThread() returned a nullptr thread");
+ thread_sp->SetStoppedBySignal(SIGSTOP);
+ ThreadWasCreated(*thread_sp);
- // Mark the inferior as invalid.
- // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
- SetState (StateType::eStateInvalid);
+ // Let our process instance know the thread has stopped.
+ SetCurrentThreadID(thread_sp->GetID());
+ SetState(StateType::eStateStopped);
- return error;
- }
- else if (WIFEXITED(status))
- {
- auto p = DecodeChildExitCode(WEXITSTATUS(status));
- Error child_error(p.second, eErrorTypePOSIX);
- const char *failure_reason;
- switch (p.first)
- {
- case ePtraceFailed:
- failure_reason = "Child ptrace failed";
- break;
- case eDupStdinFailed:
- failure_reason = "Child open stdin failed";
- break;
- case eDupStdoutFailed:
- failure_reason = "Child open stdout failed";
- break;
- case eDupStderrFailed:
- failure_reason = "Child open stderr failed";
- break;
- case eChdirFailed:
- failure_reason = "Child failed to set working directory";
- break;
- case eExecFailed:
- failure_reason = "Child exec failed";
- break;
- case eSetGidFailed:
- failure_reason = "Child setgid failed";
- break;
- case eSetSigMaskFailed:
- failure_reason = "Child failed to set signal mask";
- break;
+ if (log) {
+ if (error.Success())
+ log->Printf("NativeProcessLinux::%s inferior launching succeeded",
+ __FUNCTION__);
+ else
+ log->Printf("NativeProcessLinux::%s inferior launching failed: %s",
+ __FUNCTION__, error.AsCString());
+ }
+ return error;
+}
+
+::pid_t NativeProcessLinux::Attach(lldb::pid_t pid, Error &error) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ // Use a map to keep track of the threads which we have attached/need to
+ // attach.
+ Host::TidMap tids_to_attach;
+ if (pid <= 1) {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Attaching to process 1 is not allowed.");
+ return -1;
+ }
+
+ 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.
+ error = PtraceWrapper(PTRACE_ATTACH, tid);
+ if (error.Fail()) {
+ // No such thread. The thread may have exited.
+ // More error handling may be needed.
+ if (error.GetError() == ESRCH) {
+ it = tids_to_attach.erase(it);
+ continue;
+ } else
+ return -1;
}
- error.SetErrorStringWithFormat("%s: %d - %s (error code truncated)", failure_reason, child_error.GetError(), child_error.AsCString());
- if (log)
- {
- log->Printf ("NativeProcessLinux::%s inferior exited with status %d before issuing a STOP",
- __FUNCTION__,
- WEXITSTATUS(status));
+ int status;
+ // 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 ((status = waitpid(tid, NULL, __WALL)) < 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;
+ } else {
+ error.SetErrorToErrno();
+ return -1;
+ }
}
- // Mark the inferior as invalid.
- // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
- SetState (StateType::eStateInvalid);
-
- return error;
- }
- assert(WIFSTOPPED(status) && (wpid == static_cast< ::pid_t> (pid)) &&
- "Could not sync with inferior process.");
-
- if (log)
- log->Printf ("NativeProcessLinux::%s inferior started, now in stopped state", __FUNCTION__);
+ error = SetDefaultPtraceOpts(tid);
+ if (error.Fail())
+ return -1;
- error = SetDefaultPtraceOpts(pid);
- if (error.Fail())
- {
if (log)
- log->Printf ("NativeProcessLinux::%s inferior failed to set default ptrace options: %s",
- __FUNCTION__, error.AsCString ());
+ log->Printf("NativeProcessLinux::%s() adding tid = %" PRIu64,
+ __FUNCTION__, tid);
- // Mark the inferior as invalid.
- // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
- SetState (StateType::eStateInvalid);
+ it->second = true;
- return error;
- }
+ // Create the thread, mark it as stopped.
+ NativeThreadLinuxSP thread_sp(AddThread(static_cast<lldb::tid_t>(tid)));
+ assert(thread_sp && "AddThread() returned a nullptr");
- // Release the master terminal descriptor and pass it off to the
- // NativeProcessLinux instance. Similarly stash the inferior pid.
- m_terminal_fd = terminal.ReleaseMasterFileDescriptor();
- m_pid = pid;
- launch_info.SetProcessID(pid);
-
- // Set the terminal fd to be in non blocking mode (it simplifies the
- // implementation of ProcessLinux::GetSTDOUT to have a non-blocking
- // descriptor to read from).
- error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
- if (error.Fail())
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s inferior EnsureFDFlags failed for ensuring terminal O_NONBLOCK setting: %s",
- __FUNCTION__, error.AsCString ());
-
- // Mark the inferior as invalid.
- // FIXME this could really use a new state - eStateLaunchFailure. For now, using eStateInvalid.
- SetState (StateType::eStateInvalid);
+ // This will notify this is a new thread and tell the system it is
+ // stopped.
+ thread_sp->SetStoppedBySignal(SIGSTOP);
+ ThreadWasCreated(*thread_sp);
+ SetCurrentThreadID(thread_sp->GetID());
+ }
- return error;
+ // move the loop forward
+ ++it;
}
+ }
- if (log)
- log->Printf ("NativeProcessLinux::%s() adding pid = %" PRIu64, __FUNCTION__, pid);
-
- ResolveProcessArchitecture(m_pid, m_arch);
- NativeThreadLinuxSP thread_sp = AddThread(pid);
- assert (thread_sp && "AddThread() returned a nullptr thread");
- thread_sp->SetStoppedBySignal(SIGSTOP);
- ThreadWasCreated(*thread_sp);
-
+ if (tids_to_attach.size() > 0) {
+ m_pid = pid;
// Let our process instance know the thread has stopped.
- SetCurrentThreadID (thread_sp->GetID ());
- SetState (StateType::eStateStopped);
+ SetState(StateType::eStateStopped);
+ } else {
+ error.SetErrorToGenericError();
+ error.SetErrorString("No such process.");
+ return -1;
+ }
- if (log)
- {
- if (error.Success ())
- log->Printf("NativeProcessLinux::%s inferior launching succeeded", __FUNCTION__);
- else
- log->Printf("NativeProcessLinux::%s inferior launching failed: %s", __FUNCTION__, error.AsCString());
- }
- return error;
+ return pid;
}
-::pid_t
-NativeProcessLinux::Attach(lldb::pid_t pid, Error &error)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+Error NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
+ long ptrace_opts = 0;
- // Use a map to keep track of the threads which we have attached/need to attach.
- Host::TidMap tids_to_attach;
- if (pid <= 1)
- {
- error.SetErrorToGenericError();
- error.SetErrorString("Attaching to process 1 is not allowed.");
- return -1;
- }
+ // 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;
- 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.
- error = PtraceWrapper(PTRACE_ATTACH, tid);
- if (error.Fail())
- {
- // No such thread. The thread may have exited.
- // More error handling may be needed.
- if (error.GetError() == ESRCH)
- {
- it = tids_to_attach.erase(it);
- continue;
- }
- else
- return -1;
- }
-
- int status;
- // 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 ((status = waitpid(tid, NULL, __WALL)) < 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;
- }
- else
- {
- error.SetErrorToErrno();
- return -1;
- }
- }
-
- error = SetDefaultPtraceOpts(tid);
- if (error.Fail())
- return -1;
-
- if (log)
- log->Printf ("NativeProcessLinux::%s() adding tid = %" PRIu64, __FUNCTION__, tid);
-
- it->second = true;
-
- // Create the thread, mark it as stopped.
- NativeThreadLinuxSP thread_sp (AddThread(static_cast<lldb::tid_t>(tid)));
- assert (thread_sp && "AddThread() returned a nullptr");
-
- // This will notify this is a new thread and tell the system it is stopped.
- thread_sp->SetStoppedBySignal(SIGSTOP);
- ThreadWasCreated(*thread_sp);
- SetCurrentThreadID (thread_sp->GetID ());
- }
-
- // move the loop forward
- ++it;
- }
- }
+ // 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;
- if (tids_to_attach.size() > 0)
- {
- m_pid = pid;
- // Let our process instance know the thread has stopped.
- SetState (StateType::eStateStopped);
- }
- else
- {
- error.SetErrorToGenericError();
- error.SetErrorString("No such process.");
- return -1;
- }
+ // Have the tracer notify us before execve returns
+ // (needed to disable legacy SIGTRAP generation)
+ ptrace_opts |= PTRACE_O_TRACEEXEC;
- return pid;
+ return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts);
}
-Error
-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);
-}
-
-static ExitType convert_pid_status_to_exit_type (int status)
-{
- if (WIFEXITED (status))
- return ExitType::eExitTypeExit;
- else if (WIFSIGNALED (status))
- return ExitType::eExitTypeSignal;
- else if (WIFSTOPPED (status))
- return ExitType::eExitTypeStop;
- else
- {
- // We don't know what this is.
- return ExitType::eExitTypeInvalid;
- }
+static ExitType convert_pid_status_to_exit_type(int status) {
+ if (WIFEXITED(status))
+ return ExitType::eExitTypeExit;
+ else if (WIFSIGNALED(status))
+ return ExitType::eExitTypeSignal;
+ else if (WIFSTOPPED(status))
+ return ExitType::eExitTypeStop;
+ else {
+ // We don't know what this is.
+ return ExitType::eExitTypeInvalid;
+ }
}
-static int convert_pid_status_to_return_code (int status)
-{
- if (WIFEXITED (status))
- return WEXITSTATUS (status);
- else if (WIFSIGNALED (status))
- return WTERMSIG (status);
- else if (WIFSTOPPED (status))
- return WSTOPSIG (status);
- else
- {
- // We don't know what this is.
- return ExitType::eExitTypeInvalid;
- }
+static int convert_pid_status_to_return_code(int status) {
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ return WTERMSIG(status);
+ else if (WIFSTOPPED(status))
+ return WSTOPSIG(status);
+ else {
+ // We don't know what this is.
+ return ExitType::eExitTypeInvalid;
+ }
}
// Handles all waitpid events from the inferior process.
-void
-NativeProcessLinux::MonitorCallback(lldb::pid_t pid,
- bool exited,
- int signal,
- int status)
-{
- Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS));
+void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited,
+ int signal, int 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 ());
+ // 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)
- {
+ // Handle when the thread exits.
+ if (exited) {
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s() got exit signal(%d) , tid = %" PRIu64
+ " (%s main thread)",
+ __FUNCTION__, signal, pid, is_main_thread ? "is" : "is not");
+
+ // This is a thread that exited. Ensure we're not tracking it anymore.
+ const bool thread_found = StopTrackingThread(pid);
+
+ if (is_main_thread) {
+ // We only set the exit status and notify the delegate if we haven't
+ // already set the process
+ // state to an exited state. We normally should have received a SIGTRAP |
+ // (PTRACE_EVENT_EXIT << 8)
+ // for the main thread.
+ const bool already_notified = (GetState() == StateType::eStateExited) ||
+ (GetState() == StateType::eStateCrashed);
+ if (!already_notified) {
if (log)
- log->Printf ("NativeProcessLinux::%s() got exit signal(%d) , tid = %" PRIu64 " (%s main thread)", __FUNCTION__, signal, pid, is_main_thread ? "is" : "is not");
-
- // This is a thread that exited. Ensure we're not tracking it anymore.
- const bool thread_found = StopTrackingThread (pid);
-
- if (is_main_thread)
- {
- // We only set the exit status and notify the delegate if we haven't already set the process
- // state to an exited state. We normally should have received a SIGTRAP | (PTRACE_EVENT_EXIT << 8)
- // for the main thread.
- const bool already_notified = (GetState() == StateType::eStateExited) || (GetState () == StateType::eStateCrashed);
- if (!already_notified)
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling main thread exit (%s), expected exit state already set but state was %s instead, setting exit state now", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found", StateAsCString (GetState ()));
- // The main thread exited. We're done monitoring. Report to delegate.
- SetExitStatus (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, true);
-
- // Notify delegate that our process has exited.
- SetState (StateType::eStateExited, true);
- }
- else
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " main thread now exited (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found");
- }
- }
- else
- {
- // Do we want to report to the delegate in this case? I think not. If this was an orderly
- // thread exit, we would already have received the SIGTRAP | (PTRACE_EVENT_EXIT << 8) signal,
- // and we would have done an all-stop then.
- if (log)
- log->Printf ("NativeProcessLinux::%s() tid = %" PRIu64 " handling non-main thread exit (%s)", __FUNCTION__, pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found");
- }
- 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
+ log->Printf("NativeProcessLinux::%s() tid = %" PRIu64
+ " handling main thread exit (%s), expected exit state "
+ "already set but state was %s instead, setting exit "
+ "state now",
+ __FUNCTION__, pid,
+ thread_found ? "stopped tracking thread metadata"
+ : "thread metadata not found",
+ StateAsCString(GetState()));
+ // The main thread exited. We're done monitoring. Report to delegate.
+ SetExitStatus(convert_pid_status_to_exit_type(status),
+ convert_pid_status_to_return_code(status), nullptr, true);
+
+ // Notify delegate that our process has exited.
+ SetState(StateType::eStateExited, true);
+ } else {
if (log)
- log->Printf("NativeProcessLinux::%s received notification about an unknown tid %" PRIu64 ".", __FUNCTION__, pid);
-
- if (info_err.Fail())
- {
- if (log)
- log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") GetSignalInfo failed (%s). Ingoring this notification.", __FUNCTION__, pid, info_err.AsCString());
- return;
- }
-
- if (log && (info.si_code != SI_USER || info.si_pid != 0))
- log->Printf("NativeProcessLinux::%s (tid %" PRIu64 ") unexpected signal info (si_code: %d, si_pid: %d). Treating as a new thread notification anyway.", __FUNCTION__, pid, info.si_code, info.si_pid);
-
- auto thread_sp = AddThread(pid);
- // Resume the newly created thread.
- ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
- ThreadWasCreated(*thread_sp);
- 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);
- }
+ log->Printf("NativeProcessLinux::%s() tid = %" PRIu64
+ " main thread now exited (%s)",
+ __FUNCTION__, pid,
+ thread_found ? "stopped tracking thread metadata"
+ : "thread metadata not found");
+ }
+ } else {
+ // Do we want to report to the delegate in this case? I think not. If
+ // this was an orderly
+ // thread exit, we would already have received the SIGTRAP |
+ // (PTRACE_EVENT_EXIT << 8) signal,
+ // and we would have done an all-stop then.
+ if (log)
+ log->Printf("NativeProcessLinux::%s() tid = %" PRIu64
+ " handling non-main thread exit (%s)",
+ __FUNCTION__, pid,
+ thread_found ? "stopped tracking thread metadata"
+ : "thread metadata not found");
+ }
+ 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
+ if (log)
+ log->Printf("NativeProcessLinux::%s received notification about an "
+ "unknown tid %" PRIu64 ".",
+ __FUNCTION__, pid);
+
+ if (info_err.Fail()) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid %" PRIu64
+ ") GetSignalInfo failed (%s). Ingoring this notification.",
+ __FUNCTION__, pid, info_err.AsCString());
+ return;
+ }
+
+ if (log && (info.si_code != SI_USER || info.si_pid != 0))
+ log->Printf("NativeProcessLinux::%s (tid %" PRIu64
+ ") unexpected signal info (si_code: %d, si_pid: %d). "
+ "Treating as a new thread notification anyway.",
+ __FUNCTION__, pid, info.si_code, info.si_pid);
+
+ auto thread_sp = AddThread(pid);
+ // Resume the newly created thread.
+ ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+ ThreadWasCreated(*thread_sp);
+ 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
- {
- 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.
- if (log)
- log->Printf("NativeProcessLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64 ". Transparent handling of group stops not supported, resuming the thread.", __FUNCTION__, 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);
-
- if (log)
- log->Printf ("NativeProcessLinux::%s GetSignalInfo failed: %s, tid = %" PRIu64 ", signal = %d, status = %d (%s, %s, %s)",
- __FUNCTION__, info_err.AsCString(), pid, signal, status, info_err.GetError() == ESRCH ? "thread/process killed" : "unknown reason", is_main_thread ? "is main thread" : "is not main thread", thread_found ? "thread metadata removed" : "thread metadata not 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 (convert_pid_status_to_exit_type (status), convert_pid_status_to_return_code (status), nullptr, 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?
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 " non-main thread exit occurred, didn't tell delegate anything since thread disappeared out from underneath us", __FUNCTION__, GetID (), pid);
- }
- }
- }
-}
-
-void
-NativeProcessLinux::WaitForNewThread(::pid_t tid)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
-
- NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid);
-
- if (new_thread_sp)
- {
- // 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;
- ::pid_t wait_pid;
- do
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s() received thread creation event for tid %" PRIu32 ". tid not tracked yet, waiting for thread to appear...", __FUNCTION__, tid);
- wait_pid = waitpid(tid, &status, __WALL);
- }
- while (wait_pid == -1 && errno == EINTR);
- // 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) {
- if (log)
- log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime", __FUNCTION__, 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))
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s() waiting for tid %" PRIu32 " returned an 'exited' event. Not tracking the thread.", __FUNCTION__, tid);
- // Also a very improbable event.
- return;
- }
-
- siginfo_t info;
- Error error = GetSignalInfo(tid, &info);
- if (error.Fail())
- {
+ 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.
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s received a group stop for pid %" PRIu64
+ " tid %" PRIu64 ". Transparent handling of group stops not "
+ "supported, resuming the thread.",
+ __FUNCTION__, 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);
+
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s GetSignalInfo failed: %s, tid = %" PRIu64
+ ", signal = %d, status = %d (%s, %s, %s)",
+ __FUNCTION__, info_err.AsCString(), pid, signal, status,
+ info_err.GetError() == ESRCH ? "thread/process killed"
+ : "unknown reason",
+ is_main_thread ? "is main thread" : "is not main thread",
+ thread_found ? "thread metadata removed"
+ : "thread metadata not 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(convert_pid_status_to_exit_type(status),
+ convert_pid_status_to_return_code(status), nullptr, 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?
if (log)
- log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " failed. Assuming the thread has disappeared in the meantime.", __FUNCTION__, tid);
- return;
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64
+ " non-main thread exit occurred, didn't tell delegate "
+ "anything since thread disappeared out from underneath "
+ "us",
+ __FUNCTION__, GetID(), pid);
+ }
}
-
- if (((info.si_pid != 0) || (info.si_code != SI_USER)) && log)
- {
- // We should be getting a thread creation signal here, but we received something
- // else. There isn't much we can do about it now, so we will just log that. Since the
- // thread is alive and we are receiving events from it, we shall pretend that it was
- // created properly.
- log->Printf ("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32 " received unexpected signal with code %d from pid %d.", __FUNCTION__, tid, info.si_code, info.si_pid);
- }
-
- if (log)
- log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 ": tracking new thread tid %" PRIu32,
- __FUNCTION__, GetID (), tid);
-
- new_thread_sp = AddThread(tid);
- ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
- ThreadWasCreated(*new_thread_sp);
+ }
}
-void
-NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
- const bool is_main_thread = (thread.GetID() == GetID ());
+void NativeProcessLinux::WaitForNewThread(::pid_t tid) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
- assert(info.si_signo == SIGTRAP && "Unexpected child signal!");
+ NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid);
- 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)):
+ if (new_thread_sp) {
+ // We are already tracking the thread - we got the event on the new thread
+ // (see
+ // MonitorSignal) before this one. We are done.
+ return;
+ }
- 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())
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " received thread creation event but GetEventMessage failed so we don't know the new tid", __FUNCTION__, thread.GetID());
- } else
- WaitForNewThread(event_message);
+ // The thread is not tracked yet, let's wait for it to appear.
+ int status = -1;
+ ::pid_t wait_pid;
+ do {
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received thread creation event for "
+ "tid %" PRIu32
+ ". tid not tracked yet, waiting for thread to appear...",
+ __FUNCTION__, tid);
+ wait_pid = waitpid(tid, &status, __WALL);
+ } while (wait_pid == -1 && errno == EINTR);
+ // 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) {
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s() waiting for tid %" PRIu32
+ " failed. Assuming the thread has disappeared in the meantime",
+ __FUNCTION__, 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)) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s() waiting for tid %" PRIu32
+ " returned an 'exited' event. Not tracking the thread.",
+ __FUNCTION__, tid);
+ // Also a very improbable event.
+ return;
+ }
+
+ siginfo_t info;
+ Error error = GetSignalInfo(tid, &info);
+ if (error.Fail()) {
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32
+ " failed. Assuming the thread has disappeared in the meantime.",
+ __FUNCTION__, tid);
+ return;
+ }
+
+ if (((info.si_pid != 0) || (info.si_code != SI_USER)) && log) {
+ // We should be getting a thread creation signal here, but we received
+ // something
+ // else. There isn't much we can do about it now, so we will just log that.
+ // Since the
+ // thread is alive and we are receiving events from it, we shall pretend
+ // that it was
+ // created properly.
+ log->Printf("NativeProcessLinux::%s() GetSignalInfo for tid %" PRIu32
+ " received unexpected signal with code %d from pid %d.",
+ __FUNCTION__, tid, info.si_code, info.si_pid);
+ }
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid = %" PRIu64
+ ": tracking new thread tid %" PRIu32,
+ __FUNCTION__, GetID(), tid);
+
+ new_thread_sp = AddThread(tid);
+ ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+ ThreadWasCreated(*new_thread_sp);
+}
+
+void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info,
+ NativeThreadLinux &thread) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_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()) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid %" PRIu64
+ " received thread creation event but GetEventMessage "
+ "failed so we don't know the new tid",
+ __FUNCTION__, thread.GetID());
+ } else
+ WaitForNewThread(event_message);
+
+ ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
+ break;
+ }
+
+ case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): {
+ NativeThreadLinuxSP main_thread_sp;
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received exec event, code = %d",
+ __FUNCTION__, info.si_code ^ SIGTRAP);
- ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
- break;
- }
+ // Exec clears any pending notifications.
+ m_pending_notification_tid = LLDB_INVALID_THREAD_ID;
- case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)):
- {
- NativeThreadLinuxSP main_thread_sp;
+ // Remove all but the main thread here. Linux fork creates a new process
+ // which only copies the main thread.
+ if (log)
+ log->Printf("NativeProcessLinux::%s exec received, stop tracking all but "
+ "main thread",
+ __FUNCTION__);
+
+ for (auto thread_sp : m_threads) {
+ const bool is_main_thread = thread_sp && thread_sp->GetID() == GetID();
+ if (is_main_thread) {
+ main_thread_sp = std::static_pointer_cast<NativeThreadLinux>(thread_sp);
if (log)
- log->Printf ("NativeProcessLinux::%s() received exec event, code = %d", __FUNCTION__, 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.
+ log->Printf(
+ "NativeProcessLinux::%s found main thread with tid %" PRIu64
+ ", keeping",
+ __FUNCTION__, main_thread_sp->GetID());
+ } else {
if (log)
- log->Printf ("NativeProcessLinux::%s exec received, stop tracking all but main thread", __FUNCTION__);
-
- for (auto thread_sp : m_threads)
- {
- const bool is_main_thread = thread_sp && thread_sp->GetID () == GetID ();
- if (is_main_thread)
- {
- main_thread_sp = std::static_pointer_cast<NativeThreadLinux>(thread_sp);
- if (log)
- log->Printf ("NativeProcessLinux::%s found main thread with tid %" PRIu64 ", keeping", __FUNCTION__, main_thread_sp->GetID ());
- }
- else
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s discarding non-main-thread tid %" PRIu64 " due to exec", __FUNCTION__, thread_sp->GetID ());
- }
- }
-
- m_threads.clear ();
-
- if (main_thread_sp)
- {
- m_threads.push_back (main_thread_sp);
- SetCurrentThreadID (main_thread_sp->GetID ());
- main_thread_sp->SetStoppedByExec();
- }
- else
- {
- SetCurrentThreadID (LLDB_INVALID_THREAD_ID);
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 "no main thread found, discarded all threads, we're in a no-thread state!", __FUNCTION__, GetID ());
- }
-
- // Tell coordinator about about the "new" (since exec) stopped main thread.
- ThreadWasCreated(*main_thread_sp);
-
- // Let our delegate know we have just exec'd.
- NotifyDidExec ();
-
- // If we have a main thread, indicate we are stopped.
- assert (main_thread_sp && "exec called during ptraced process but no main thread metadata tracked");
-
- // Let the process know we're stopped.
- StopRunningThreads(main_thread_sp->GetID());
-
- break;
+ log->Printf(
+ "NativeProcessLinux::%s discarding non-main-thread tid %" PRIu64
+ " due to exec",
+ __FUNCTION__, thread_sp->GetID());
+ }
}
- 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;
-
- if (log)
- {
- log->Printf ("NativeProcessLinux::%s() received PTRACE_EVENT_EXIT, data = %lx (WIFEXITED=%s,WIFSIGNALED=%s), pid = %" PRIu64 " (%s)",
- __FUNCTION__,
- data, WIFEXITED (data) ? "true" : "false", WIFSIGNALED (data) ? "true" : "false",
- thread.GetID(),
- is_main_thread ? "is main thread" : "not main thread");
- }
+ m_threads.clear();
- if (is_main_thread)
- {
- SetExitStatus (convert_pid_status_to_exit_type (data), convert_pid_status_to_return_code (data), nullptr, true);
- }
-
- 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;
- Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, (uintptr_t)info.si_addr);
- if (error.Fail() && log)
- log->Printf("NativeProcessLinux::%s() "
- "received error while checking for watchpoint hits, "
- "pid = %" PRIu64 " error = %s",
- __FUNCTION__, thread.GetID(), error.AsCString());
- if (wp_index != LLDB_INVALID_INDEX32)
- {
- MonitorWatchpoint(thread, wp_index);
- break;
- }
-
- // Otherwise, report step over
- MonitorTrace(thread);
- break;
- }
-
- case SI_KERNEL:
+ if (main_thread_sp) {
+ m_threads.push_back(main_thread_sp);
+ SetCurrentThreadID(main_thread_sp->GetID());
+ main_thread_sp->SetStoppedByExec();
+ } else {
+ SetCurrentThreadID(LLDB_INVALID_THREAD_ID);
+ if (log)
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64
+ "no main thread found, discarded all threads, we're in a "
+ "no-thread state!",
+ __FUNCTION__, GetID());
+ }
+
+ // Tell coordinator about about the "new" (since exec) stopped main thread.
+ ThreadWasCreated(*main_thread_sp);
+
+ // Let our delegate know we have just exec'd.
+ NotifyDidExec();
+
+ // If we have a main thread, indicate we are stopped.
+ assert(main_thread_sp && "exec called during ptraced process but no main "
+ "thread metadata tracked");
+
+ // Let the process know we're stopped.
+ StopRunningThreads(main_thread_sp->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;
+
+ if (log) {
+ log->Printf("NativeProcessLinux::%s() received PTRACE_EVENT_EXIT, data = "
+ "%lx (WIFEXITED=%s,WIFSIGNALED=%s), pid = %" PRIu64 " (%s)",
+ __FUNCTION__, data, WIFEXITED(data) ? "true" : "false",
+ WIFSIGNALED(data) ? "true" : "false", thread.GetID(),
+ is_main_thread ? "is main thread" : "not main thread");
+ }
+
+ if (is_main_thread) {
+ SetExitStatus(convert_pid_status_to_exit_type(data),
+ convert_pid_status_to_return_code(data), nullptr, true);
+ }
+
+ 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;
+ Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(
+ wp_index, (uintptr_t)info.si_addr);
+ if (error.Fail() && log)
+ log->Printf("NativeProcessLinux::%s() "
+ "received error while checking for watchpoint hits, "
+ "pid = %" PRIu64 " error = %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
+ if (wp_index != LLDB_INVALID_INDEX32) {
+ MonitorWatchpoint(thread, wp_index);
+ 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;
- Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(wp_index, LLDB_INVALID_ADDRESS);
- if (error.Fail() && log)
- log->Printf("NativeProcessLinux::%s() "
- "received error while checking for watchpoint hits, "
- "pid = %" PRIu64 " error = %s",
- __FUNCTION__, thread.GetID(), error.AsCString());
- if (wp_index != LLDB_INVALID_INDEX32)
- {
- MonitorWatchpoint(thread, wp_index);
- break;
- }
+ // 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;
+ Error error = thread.GetRegisterContext()->GetWatchpointHitIndex(
+ wp_index, LLDB_INVALID_ADDRESS);
+ if (error.Fail() && log)
+ log->Printf("NativeProcessLinux::%s() "
+ "received error while checking for watchpoint hits, "
+ "pid = %" PRIu64 " error = %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
+ if (wp_index != LLDB_INVALID_INDEX32) {
+ MonitorWatchpoint(thread, wp_index);
+ break;
+ }
}
- // NO BREAK
+// NO BREAK
#endif
- case TRAP_BRKPT:
- MonitorBreakpoint(thread);
- break;
+ case TRAP_BRKPT:
+ MonitorBreakpoint(thread);
+ break;
- case SIGTRAP:
- case (SIGTRAP | 0x80):
- if (log)
- log->Printf ("NativeProcessLinux::%s() received unknown SIGTRAP system call stop event, pid %" PRIu64 "tid %" PRIu64 ", resuming", __FUNCTION__, GetID (), thread.GetID());
+ case SIGTRAP:
+ case (SIGTRAP | 0x80):
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received unknown SIGTRAP system "
+ "call stop event, pid %" PRIu64 "tid %" PRIu64 ", resuming",
+ __FUNCTION__, GetID(), thread.GetID());
- // Ignore these signals until we know more about them.
- ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
- break;
+ // Ignore these signals until we know more about them.
+ ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
+ break;
- default:
- assert(false && "Unexpected SIGTRAP code!");
- if (log)
- log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 "tid %" PRIu64 " received unhandled SIGTRAP code: 0x%d",
- __FUNCTION__, GetID(), thread.GetID(), info.si_code);
- break;
-
- }
-}
-
-void
-NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread)
-{
- Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ default:
+ assert(false && "Unexpected SIGTRAP code!");
if (log)
- log->Printf("NativeProcessLinux::%s() received trace event, pid = %" PRIu64 " (single stepping)",
+ log->Printf("NativeProcessLinux::%s() pid %" PRIu64 "tid %" PRIu64
+ " received unhandled SIGTRAP code: 0x%d",
+ __FUNCTION__, GetID(), thread.GetID(), info.si_code);
+ break;
+ }
+}
+
+void NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received trace event, pid = %" PRIu64
+ " (single stepping)",
__FUNCTION__, thread.GetID());
- // This thread is currently stopped.
- thread.SetStoppedByTrace();
+ // This thread is currently stopped.
+ thread.SetStoppedByTrace();
- StopRunningThreads(thread.GetID());
+ StopRunningThreads(thread.GetID());
}
-void
-NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread)
-{
- Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
- if (log)
- log->Printf("NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64,
- __FUNCTION__, thread.GetID());
+void NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s() received breakpoint event, pid = %" PRIu64,
+ __FUNCTION__, thread.GetID());
- // Mark the thread as stopped at breakpoint.
- thread.SetStoppedByBreakpoint();
- Error error = FixupBreakpointPCAsNeeded(thread);
- if (error.Fail())
- if (log)
- log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s",
- __FUNCTION__, thread.GetID(), error.AsCString());
+ // Mark the thread as stopped at breakpoint.
+ thread.SetStoppedByBreakpoint();
+ Error error = FixupBreakpointPCAsNeeded(thread);
+ if (error.Fail())
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " fixup: %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
- if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != m_threads_stepping_with_breakpoint.end())
- thread.SetStoppedByTrace();
+ if (m_threads_stepping_with_breakpoint.find(thread.GetID()) !=
+ m_threads_stepping_with_breakpoint.end())
+ thread.SetStoppedByTrace();
- StopRunningThreads(thread.GetID());
+ StopRunningThreads(thread.GetID());
}
-void
-NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index)
-{
- Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS));
- if (log)
- log->Printf("NativeProcessLinux::%s() received watchpoint event, "
- "pid = %" PRIu64 ", wp_index = %" PRIu32,
- __FUNCTION__, thread.GetID(), wp_index);
+void NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread,
+ uint32_t wp_index) {
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received watchpoint event, "
+ "pid = %" PRIu64 ", wp_index = %" PRIu32,
+ __FUNCTION__, 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);
+ // 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());
+ // 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 (GetLogIfAllCategoriesSet (LIBLLDB_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.
- if (info.si_code == SI_TKILL || info.si_code == SI_USER)
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s() received signal %s (%d) with code %s, (siginfo pid = %d (%s), waitpid pid = %" PRIu64 ")",
- __FUNCTION__,
- Host::GetSignalAsCString(signo),
- signo,
- (info.si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"),
- info.si_pid,
- is_from_llgs ? "from llgs" : "not from llgs",
- 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();
- // Check for thread stop notification.
- if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP))
- {
- // This is a tgkill()-based stop.
- if (log)
- log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread stopped",
- __FUNCTION__,
- 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.
- Error error = ResumeThread(thread, thread.GetState(), 0);
- if (error.Fail() && log)
- {
- log->Printf("NativeProcessLinux::%s failed to resume thread tid %" PRIu64 ": %s",
- __FUNCTION__, thread.GetID(), error.AsCString());
- }
- }
- }
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_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.
+ if (info.si_code == SI_TKILL || info.si_code == SI_USER) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received signal %s (%d) with code "
+ "%s, (siginfo pid = %d (%s), waitpid pid = %" PRIu64 ")",
+ __FUNCTION__, Host::GetSignalAsCString(signo), signo,
+ (info.si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"),
+ info.si_pid, is_from_llgs ? "from llgs" : "not from llgs",
+ thread.GetID());
+ }
+
+ // Check for thread stop notification.
+ if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP)) {
+ // This is a tgkill()-based stop.
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64
+ ", thread stopped",
+ __FUNCTION__, 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
- {
- if (log)
- {
- // Retrieve the signal name if the thread was stopped by a signal.
- int stop_signo = 0;
- const bool stopped_by_signal = thread.IsStopped(&stop_signo);
- const char *signal_name = stopped_by_signal ? Host::GetSignalAsCString(stop_signo) : "<not stopped by signal>";
- if (!signal_name)
- signal_name = "<no-signal-name>";
-
- log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", thread was already marked as a stopped state (state=%s, signal=%d (%s)), leaving stop signal as is",
- __FUNCTION__,
- GetID (),
- thread.GetID(),
- StateAsCString (thread_state),
- stop_signo,
- signal_name);
- }
- SignalIfAllThreadsStopped();
+ 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.
+ Error error = ResumeThread(thread, thread.GetState(), 0);
+ if (error.Fail() && log) {
+ log->Printf(
+ "NativeProcessLinux::%s failed to resume thread tid %" PRIu64
+ ": %s",
+ __FUNCTION__, thread.GetID(), error.AsCString());
}
+ }
+ } else {
+ if (log) {
+ // Retrieve the signal name if the thread was stopped by a signal.
+ int stop_signo = 0;
+ const bool stopped_by_signal = thread.IsStopped(&stop_signo);
+ const char *signal_name = stopped_by_signal
+ ? Host::GetSignalAsCString(stop_signo)
+ : "<not stopped by signal>";
+ if (!signal_name)
+ signal_name = "<no-signal-name>";
- // Done handling.
- return;
+ log->Printf("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64
+ ", thread was already marked as a stopped state (state=%s, "
+ "signal=%d (%s)), leaving stop signal as is",
+ __FUNCTION__, GetID(), thread.GetID(),
+ StateAsCString(thread_state), stop_signo, signal_name);
+ }
+ SignalIfAllThreadsStopped();
}
- if (log)
- log->Printf ("NativeProcessLinux::%s() received signal %s", __FUNCTION__, Host::GetSignalAsCString(signo));
+ // Done handling.
+ return;
+ }
- // This thread is stopped.
- thread.SetStoppedBySignal(signo, &info);
+ if (log)
+ log->Printf("NativeProcessLinux::%s() received signal %s", __FUNCTION__,
+ Host::GetSignalAsCString(signo));
- // Send a stop to the debugger after we get all other threads to stop.
- StopRunningThreads(thread.GetID());
+ // This thread is stopped.
+ 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;
+struct EmulatorBaton {
+ NativeProcessLinux *m_process;
+ NativeRegisterContext *m_reg_context;
- // eRegisterKindDWARF -> RegsiterValue
- std::unordered_map<uint32_t, RegisterValue> m_register_values;
+ // eRegisterKindDWARF -> RegsiterValue
+ std::unordered_map<uint32_t, RegisterValue> m_register_values;
- EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) :
- m_process(process), m_reg_context(reg_context) {}
+ EmulatorBaton(NativeProcessLinux *process, NativeRegisterContext *reg_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);
+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;
+ 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 &reg_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;
- }
+static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
- // 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]);
-
- Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
- if (error.Success())
- return true;
+ 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]);
+
+ Error error =
+ emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value);
+ if (error.Success())
+ return true;
- return false;
+ return false;
}
-static bool
-WriteRegisterCallback (EmulateInstruction *instruction,
- void *baton,
- const EmulateInstruction::Context &context,
- const RegisterInfo *reg_info,
- const RegisterValue &reg_value)
-{
- EmulatorBaton* emulator_baton = static_cast<EmulatorBaton*>(baton);
- emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value;
- return true;
+static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const EmulateInstruction::Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_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 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* regsiter_context)
-{
- const RegisterInfo* flags_info = regsiter_context->GetRegisterInfo(
- eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
- return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS);
+static lldb::addr_t ReadFlags(NativeRegisterContext *regsiter_context) {
+ const RegisterInfo *flags_info = regsiter_context->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+ return regsiter_context->ReadRegisterAsUnsigned(flags_info,
+ LLDB_INVALID_ADDRESS);
}
-Error
-NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread)
-{
- Error error;
- NativeRegisterContextSP register_context_sp = thread.GetRegisterContext();
+Error NativeProcessLinux::SetupSoftwareSingleStepping(
+ NativeThreadLinux &thread) {
+ Error error;
+ NativeRegisterContextSP register_context_sp = thread.GetRegisterContext();
- std::unique_ptr<EmulateInstruction> emulator_ap(
- EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr));
+ std::unique_ptr<EmulateInstruction> emulator_ap(
+ EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying,
+ nullptr));
- if (emulator_ap == nullptr)
- return Error("Instruction emulator not found!");
+ if (emulator_ap == nullptr)
+ return Error("Instruction emulator not found!");
- EmulatorBaton baton(this, register_context_sp.get());
- emulator_ap->SetBaton(&baton);
- emulator_ap->SetReadMemCallback(&ReadMemoryCallback);
- emulator_ap->SetReadRegCallback(&ReadRegisterCallback);
- emulator_ap->SetWriteMemCallback(&WriteMemoryCallback);
- emulator_ap->SetWriteRegCallback(&WriteRegisterCallback);
+ EmulatorBaton baton(this, register_context_sp.get());
+ emulator_ap->SetBaton(&baton);
+ emulator_ap->SetReadMemCallback(&ReadMemoryCallback);
+ emulator_ap->SetReadRegCallback(&ReadRegisterCallback);
+ emulator_ap->SetWriteMemCallback(&WriteMemoryCallback);
+ emulator_ap->SetWriteRegCallback(&WriteRegisterCallback);
- if (!emulator_ap->ReadInstruction())
- return Error("Read instruction failed!");
+ if (!emulator_ap->ReadInstruction())
+ return Error("Read instruction failed!");
- bool emulation_result = emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
+ bool emulation_result =
+ emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
- const RegisterInfo* reg_info_pc = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
- const RegisterInfo* reg_info_flags = register_context_sp->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+ const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ const RegisterInfo *reg_info_flags = register_context_sp->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]);
+ 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();
+ 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_sp.get());
- }
- else if (pc_it == baton.m_register_values.end())
- {
- // Emulate instruction failed and it haven't changed PC. Advance PC
- // with the size of the current opcode because the emulation of all
- // PC modifying instruction should be successful. The failure most
- // likely caused by a not supported instruction which don't modify PC.
- next_pc = register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize();
- next_flags = ReadFlags (register_context_sp.get());
- }
+ if (flags_it != baton.m_register_values.end())
+ next_flags = flags_it->second.GetAsUInt64();
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 Error ("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)
- error = SetSoftwareBreakpoint(next_pc, 4);
- else
- {
- // No size hint is given for the next breakpoint
- error = SetSoftwareBreakpoint(next_pc, 0);
- }
-
- if (error.Fail())
- return error;
+ next_flags = ReadFlags(register_context_sp.get());
+ } else if (pc_it == baton.m_register_values.end()) {
+ // Emulate instruction failed and it haven't changed PC. Advance PC
+ // with the size of the current opcode because the emulation of all
+ // PC modifying instruction should be successful. The failure most
+ // likely caused by a not supported instruction which don't modify PC.
+ next_pc =
+ register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize();
+ next_flags = ReadFlags(register_context_sp.get());
+ } 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 Error("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)
+ 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 Error();
+ } else if (error.Fail())
+ return error;
- m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
+ m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
- return Error();
+ return Error();
}
-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;
+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;
}
-Error
-NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
- if (log)
- log->Printf ("NativeProcessLinux::%s called: pid %" PRIu64, __FUNCTION__, GetID ());
+Error NativeProcessLinux::Resume(const ResumeActionList &resume_actions) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("NativeProcessLinux::%s called: pid %" PRIu64, __FUNCTION__,
+ GetID());
- bool software_single_step = !SupportHardwareSingleStepping();
+ bool software_single_step = !SupportHardwareSingleStepping();
- if (software_single_step)
- {
- for (auto thread_sp : m_threads)
- {
- assert (thread_sp && "thread list should not contain NULL threads");
-
- const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true);
- if (action == nullptr)
- continue;
-
- if (action->state == eStateStepping)
- {
- Error error = SetupSoftwareSingleStepping(static_cast<NativeThreadLinux &>(*thread_sp));
- if (error.Fail())
- return error;
- }
- }
+ if (software_single_step) {
+ for (auto thread_sp : m_threads) {
+ assert(thread_sp && "thread list should not contain NULL threads");
+
+ const ResumeAction *const action =
+ resume_actions.GetActionForThread(thread_sp->GetID(), true);
+ if (action == nullptr)
+ continue;
+
+ if (action->state == eStateStepping) {
+ Error error = SetupSoftwareSingleStepping(
+ static_cast<NativeThreadLinux &>(*thread_sp));
+ if (error.Fail())
+ return error;
+ }
}
+ }
- for (auto thread_sp : m_threads)
- {
- assert (thread_sp && "thread list should not contain NULL threads");
+ for (auto thread_sp : m_threads) {
+ assert(thread_sp && "thread list should not contain NULL threads");
- const ResumeAction *const action = resume_actions.GetActionForThread (thread_sp->GetID (), true);
+ const ResumeAction *const action =
+ resume_actions.GetActionForThread(thread_sp->GetID(), true);
- if (action == nullptr)
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s no action specified for pid %" PRIu64 " tid %" PRIu64,
- __FUNCTION__, GetID (), thread_sp->GetID ());
- continue;
- }
+ if (action == nullptr) {
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s no action specified for pid %" PRIu64
+ " tid %" PRIu64,
+ __FUNCTION__, GetID(), thread_sp->GetID());
+ continue;
+ }
- if (log)
- {
- log->Printf ("NativeProcessLinux::%s processing resume action state %s for pid %" PRIu64 " tid %" PRIu64,
- __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ());
- }
+ if (log) {
+ log->Printf("NativeProcessLinux::%s processing resume action state %s "
+ "for pid %" PRIu64 " tid %" PRIu64,
+ __FUNCTION__, StateAsCString(action->state), GetID(),
+ thread_sp->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_sp), action->state, signo);
- break;
- }
+ 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_sp), action->state,
+ signo);
+ break;
+ }
- case eStateSuspended:
- case eStateStopped:
- lldbassert(0 && "Unexpected state");
+ case eStateSuspended:
+ case eStateStopped:
+ lldbassert(0 && "Unexpected state");
- default:
- return Error ("NativeProcessLinux::%s (): unexpected state %s specified for pid %" PRIu64 ", tid %" PRIu64,
- __FUNCTION__, StateAsCString (action->state), GetID (), thread_sp->GetID ());
- }
+ default:
+ return Error("NativeProcessLinux::%s (): unexpected state %s specified "
+ "for pid %" PRIu64 ", tid %" PRIu64,
+ __FUNCTION__, StateAsCString(action->state), GetID(),
+ thread_sp->GetID());
}
+ }
- return Error();
+ return Error();
}
-Error
-NativeProcessLinux::Halt ()
-{
- Error error;
+Error NativeProcessLinux::Halt() {
+ Error error;
- if (kill (GetID (), SIGSTOP) != 0)
- error.SetErrorToErrno ();
+ if (kill(GetID(), SIGSTOP) != 0)
+ error.SetErrorToErrno();
- return error;
+ return error;
}
-Error
-NativeProcessLinux::Detach ()
-{
- Error error;
+Error NativeProcessLinux::Detach() {
+ Error error;
- // Stop monitoring the inferior.
- m_sigchld_handle.reset();
+ // Stop monitoring the inferior.
+ m_sigchld_handle.reset();
- // Tell ptrace to detach from the process.
- if (GetID () == LLDB_INVALID_PROCESS_ID)
- return error;
+ // Tell ptrace to detach from the process.
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ return error;
- for (auto thread_sp : m_threads)
- {
- Error e = Detach(thread_sp->GetID());
- if (e.Fail())
- error = e; // Save the error, but still attempt to detach from other threads.
- }
+ for (auto thread_sp : m_threads) {
+ Error e = Detach(thread_sp->GetID());
+ if (e.Fail())
+ error =
+ e; // Save the error, but still attempt to detach from other threads.
+ }
- return error;
+ return error;
}
-Error
-NativeProcessLinux::Signal (int signo)
-{
- Error error;
+Error NativeProcessLinux::Signal(int signo) {
+ Error error;
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
- if (log)
- log->Printf ("NativeProcessLinux::%s: sending signal %d (%s) to pid %" PRIu64,
- __FUNCTION__, signo, Host::GetSignalAsCString(signo), GetID());
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s: sending signal %d (%s) to pid %" PRIu64,
+ __FUNCTION__, signo, Host::GetSignalAsCString(signo), GetID());
- if (kill(GetID(), signo))
- error.SetErrorToErrno();
+ if (kill(GetID(), signo))
+ error.SetErrorToErrno();
- return error;
+ return error;
}
-Error
-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 (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+Error 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(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
- NativeThreadProtocolSP running_thread_sp;
- NativeThreadProtocolSP stopped_thread_sp;
-
- if (log)
- log->Printf ("NativeProcessLinux::%s selecting running thread for interrupt target", __FUNCTION__);
+ NativeThreadProtocolSP running_thread_sp;
+ NativeThreadProtocolSP stopped_thread_sp;
- for (auto thread_sp : m_threads)
- {
- // The thread shouldn't be null but lets just cover that here.
- if (!thread_sp)
- continue;
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s selecting running thread for interrupt target",
+ __FUNCTION__);
- // If we have a running or stepping thread, we'll call that the
- // target of the interrupt.
- const auto thread_state = thread_sp->GetState ();
- if (thread_state == eStateRunning ||
- thread_state == eStateStepping)
- {
- running_thread_sp = thread_sp;
- break;
- }
- else if (!stopped_thread_sp && 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_sp = thread_sp;
- }
- }
+ for (auto thread_sp : m_threads) {
+ // The thread shouldn't be null but lets just cover that here.
+ if (!thread_sp)
+ continue;
- if (!running_thread_sp && !stopped_thread_sp)
- {
- Error error("found no running/stepping or live stopped threads as target for interrupt");
- if (log)
- log->Printf ("NativeProcessLinux::%s skipping due to error: %s", __FUNCTION__, error.AsCString ());
-
- return error;
+ // If we have a running or stepping thread, we'll call that the
+ // target of the interrupt.
+ const auto thread_state = thread_sp->GetState();
+ if (thread_state == eStateRunning || thread_state == eStateStepping) {
+ running_thread_sp = thread_sp;
+ break;
+ } else if (!stopped_thread_sp && 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_sp = thread_sp;
}
+ }
- NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp;
-
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " %s tid %" PRIu64 " chosen for interrupt target",
- __FUNCTION__,
- GetID (),
- running_thread_sp ? "running" : "stopped",
- deferred_signal_thread_sp->GetID ());
-
- StopRunningThreads(deferred_signal_thread_sp->GetID());
-
- return Error();
-}
-
-Error
-NativeProcessLinux::Kill ()
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (!running_thread_sp && !stopped_thread_sp) {
+ Error error("found no running/stepping or live stopped threads as target "
+ "for interrupt");
if (log)
- log->Printf ("NativeProcessLinux::%s called for PID %" PRIu64, __FUNCTION__, GetID ());
-
- Error 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.
- if (log)
- log->Printf ("NativeProcessLinux::%s ignored for PID %" PRIu64 " due to current state: %s", __FUNCTION__, GetID (), StateAsCString (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;
- }
+ log->Printf("NativeProcessLinux::%s skipping due to error: %s",
+ __FUNCTION__, error.AsCString());
return error;
-}
+ }
-static Error
-ParseMemoryRegionInfoFromProcMapsLine (const std::string &maps_line, MemoryRegionInfo &memory_region_info)
-{
- memory_region_info.Clear();
-
- StringExtractor line_extractor (maps_line.c_str ());
+ NativeThreadProtocolSP deferred_signal_thread_sp =
+ running_thread_sp ? running_thread_sp : stopped_thread_sp;
- // Format: {address_start_hex}-{address_end_hex} perms offset dev inode pathname
- // perms: rwxp (letter is present if set, '-' if not, final character is p=private, s=shared).
+ if (log)
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64 " %s tid %" PRIu64
+ " chosen for interrupt target",
+ __FUNCTION__, GetID(),
+ running_thread_sp ? "running" : "stopped",
+ deferred_signal_thread_sp->GetID());
- // Parse out the starting address
- lldb::addr_t start_address = line_extractor.GetHexMaxU64 (false, 0);
+ StopRunningThreads(deferred_signal_thread_sp->GetID());
- // Parse out hyphen separating start and end address from range.
- if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != '-'))
- return Error ("malformed /proc/{pid}/maps entry, missing dash between address range");
-
- // Parse out the ending address
- lldb::addr_t end_address = line_extractor.GetHexMaxU64 (false, start_address);
-
- // Parse out the space after the address.
- if (!line_extractor.GetBytesLeft () || (line_extractor.GetChar () != ' '))
- return Error ("malformed /proc/{pid}/maps entry, missing space after range");
+ return Error();
+}
- // Save the range.
- memory_region_info.GetRange ().SetRangeBase (start_address);
- memory_region_info.GetRange ().SetRangeEnd (end_address);
+Error NativeProcessLinux::Kill() {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s called for PID %" PRIu64, __FUNCTION__,
+ GetID());
- // Any memory region in /proc/{pid}/maps is by definition mapped into the process.
- memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+ Error error;
- // Parse out each permission entry.
- if (line_extractor.GetBytesLeft () < 4)
- return Error ("malformed /proc/{pid}/maps entry, missing some portion of permissions");
+ 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.
+ if (log)
+ log->Printf("NativeProcessLinux::%s ignored for PID %" PRIu64
+ " due to current state: %s",
+ __FUNCTION__, GetID(), StateAsCString(m_state));
+ return error;
- // Handle read permission.
- const char read_perm_char = line_extractor.GetChar ();
- if (read_perm_char == 'r')
- memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eYes);
- else if (read_perm_char == '-')
- memory_region_info.SetReadable (MemoryRegionInfo::OptionalBool::eNo);
- else
- return Error ("unexpected /proc/{pid}/maps read permission char");
-
- // Handle write permission.
- const char write_perm_char = line_extractor.GetChar ();
- if (write_perm_char == 'w')
- memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eYes);
- else if (write_perm_char == '-')
- memory_region_info.SetWritable (MemoryRegionInfo::OptionalBool::eNo);
- else
- return Error ("unexpected /proc/{pid}/maps write permission char");
-
- // Handle execute permission.
- const char exec_perm_char = line_extractor.GetChar ();
- if (exec_perm_char == 'x')
- memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eYes);
- else if (exec_perm_char == '-')
- memory_region_info.SetExecutable (MemoryRegionInfo::OptionalBool::eNo);
- else
- return Error ("unexpected /proc/{pid}/maps exec permission char");
+ 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 ();
+ return error;
}
-Error
-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.
-
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
- Error error;
-
- if (m_supports_mem_region == LazyBool::eLazyBoolNo)
- {
- // We're done.
- error.SetErrorString ("unsupported");
- return error;
- }
-
- // 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 ())
- {
- error = ProcFileReader::ProcessLineByLine (GetID (), "maps",
- [&] (const std::string &line) -> bool
- {
- MemoryRegionInfo info;
- const Error parse_error = ParseMemoryRegionInfoFromProcMapsLine (line, info);
- if (parse_error.Success ())
- {
- m_mem_region_cache.push_back (info);
- return true;
- }
- else
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s failed to parse proc maps line '%s': %s", __FUNCTION__, line.c_str (), error.AsCString ());
- return false;
- }
- });
-
- // If we had an error, we'll mark unsupported.
- if (error.Fail ())
- {
- m_supports_mem_region = LazyBool::eLazyBoolNo;
- return error;
- }
- else 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.
+static Error
+ParseMemoryRegionInfoFromProcMapsLine(const std::string &maps_line,
+ MemoryRegionInfo &memory_region_info) {
+ memory_region_info.Clear();
+
+ StringExtractor line_extractor(maps_line.c_str());
+
+ // Format: {address_start_hex}-{address_end_hex} perms offset dev inode
+ // pathname
+ // perms: rwxp (letter is present if set, '-' if not, final character is
+ // p=private, s=shared).
+
+ // Parse out the starting address
+ lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0);
+
+ // Parse out hyphen separating start and end address from range.
+ if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-'))
+ return Error(
+ "malformed /proc/{pid}/maps entry, missing dash between address range");
+
+ // Parse out the ending address
+ lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address);
+
+ // Parse out the space after the address.
+ if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' '))
+ return Error("malformed /proc/{pid}/maps entry, missing space after range");
+
+ // Save the range.
+ memory_region_info.GetRange().SetRangeBase(start_address);
+ memory_region_info.GetRange().SetRangeEnd(end_address);
+
+ // Any memory region in /proc/{pid}/maps is by definition mapped into the
+ // process.
+ memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+ // Parse out each permission entry.
+ if (line_extractor.GetBytesLeft() < 4)
+ return Error("malformed /proc/{pid}/maps entry, missing some portion of "
+ "permissions");
+
+ // Handle read permission.
+ const char read_perm_char = line_extractor.GetChar();
+ if (read_perm_char == 'r')
+ memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (read_perm_char == '-')
+ memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Error("unexpected /proc/{pid}/maps read permission char");
+
+ // Handle write permission.
+ const char write_perm_char = line_extractor.GetChar();
+ if (write_perm_char == 'w')
+ memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (write_perm_char == '-')
+ memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Error("unexpected /proc/{pid}/maps write permission char");
+
+ // Handle execute permission.
+ const char exec_perm_char = line_extractor.GetChar();
+ if (exec_perm_char == 'x')
+ memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+ else if (exec_perm_char == '-')
+ memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+ else
+ return Error("unexpected /proc/{pid}/maps exec permission char");
+
+ line_extractor.GetChar(); // Read the private bit
+ line_extractor.SkipSpaces(); // Skip the separator
+ line_extractor.GetHexMaxU64(false, 0); // Read the offset
+ line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+ line_extractor.GetChar(); // Read the device id separator
+ line_extractor.GetHexMaxU64(false, 0); // Read the major device number
+ line_extractor.SkipSpaces(); // Skip the separator
+ line_extractor.GetU64(0, 10); // Read the inode number
+
+ line_extractor.SkipSpaces();
+ const char *name = line_extractor.Peek();
+ if (name)
+ memory_region_info.SetName(name);
+
+ return Error();
+}
+
+Error 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.
+
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ Error error;
+
+ if (m_supports_mem_region == LazyBool::eLazyBoolNo) {
+ // We're done.
+ error.SetErrorString("unsupported");
+ return error;
+ }
+
+ // 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()) {
+ error = ProcFileReader::ProcessLineByLine(
+ GetID(), "maps", [&](const std::string &line) -> bool {
+ MemoryRegionInfo info;
+ const Error parse_error =
+ ParseMemoryRegionInfoFromProcMapsLine(line, info);
+ if (parse_error.Success()) {
+ m_mem_region_cache.push_back(info);
+ return true;
+ } else {
if (log)
- log->Printf ("NativeProcessLinux::%s failed to find any procfs maps entries, assuming no support for memory region metadata retrieval", __FUNCTION__);
- m_supports_mem_region = LazyBool::eLazyBoolNo;
- error.SetErrorString ("not supported");
- return error;
- }
-
- if (log)
- log->Printf ("NativeProcessLinux::%s read %" PRIu64 " memory region entries from /proc/%" PRIu64 "/maps", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()), GetID ());
-
- // We support memory retrieval, remember that.
- m_supports_mem_region = LazyBool::eLazyBoolYes;
- }
- else
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s reusing %" PRIu64 " cached memory region entries", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()));
- }
-
- 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;
-
- // 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 ();
-
- // 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;
- }
+ log->Printf("NativeProcessLinux::%s failed to parse proc maps "
+ "line '%s': %s",
+ __FUNCTION__, line.c_str(), error.AsCString());
+ return false;
+ }
+ });
- // The target memory address comes somewhere after the region we just parsed.
+ // If we had an error, we'll mark unsupported.
+ if (error.Fail()) {
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ return error;
+ } else 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.
+ if (log)
+ log->Printf("NativeProcessLinux::%s failed to find any procfs maps "
+ "entries, assuming no support for memory region metadata "
+ "retrieval",
+ __FUNCTION__);
+ m_supports_mem_region = LazyBool::eLazyBoolNo;
+ error.SetErrorString("not supported");
+ return error;
}
- // 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;
-}
-
-void
-NativeProcessLinux::DoStopIDBumped (uint32_t newBumpId)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
if (log)
- log->Printf ("NativeProcessLinux::%s(newBumpId=%" PRIu32 ") called", __FUNCTION__, newBumpId);
-
- if (log)
- log->Printf ("NativeProcessLinux::%s clearing %" PRIu64 " entries from the cache", __FUNCTION__, static_cast<uint64_t> (m_mem_region_cache.size ()));
- m_mem_region_cache.clear ();
-}
-
-Error
-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.
+ log->Printf("NativeProcessLinux::%s read %" PRIu64
+ " memory region entries from /proc/%" PRIu64 "/maps",
+ __FUNCTION__,
+ static_cast<uint64_t>(m_mem_region_cache.size()), GetID());
+
+ // We support memory retrieval, remember that.
+ m_supports_mem_region = LazyBool::eLazyBoolYes;
+ } else {
+ if (log)
+ log->Printf("NativeProcessLinux::%s reusing %" PRIu64
+ " cached memory region entries",
+ __FUNCTION__,
+ static_cast<uint64_t>(m_mem_region_cache.size()));
+ }
+
+ 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;
+
+ // 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();
+
+ // 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;
+}
+
+void NativeProcessLinux::DoStopIDBumped(uint32_t newBumpId) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s(newBumpId=%" PRIu32 ") called",
+ __FUNCTION__, newBumpId);
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s clearing %" PRIu64
+ " entries from the cache",
+ __FUNCTION__, static_cast<uint64_t>(m_mem_region_cache.size()));
+ m_mem_region_cache.clear();
+}
+
+Error 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 Error ("not implemented yet");
+ return Error("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 Error();
+ } 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 Error ();
- } else {
- addr = LLDB_INVALID_ADDRESS;
- return Error("unable to allocate %" PRIu64 " bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions));
- }
+ return Error("unable to allocate %" PRIu64
+ " bytes of memory with permissions %s",
+ size, GetPermissionsAsCString(permissions));
+ }
#endif
}
-Error
-NativeProcessLinux::DeallocateMemory (lldb::addr_t addr)
-{
- // FIXME see comments in AllocateMemory - required lower-level
- // bits not in place yet (ThreadPlans)
- return Error ("not implemented");
+Error NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) {
+ // FIXME see comments in AllocateMemory - required lower-level
+ // bits not in place yet (ThreadPlans)
+ return Error("not implemented");
}
-lldb::addr_t
-NativeProcessLinux::GetSharedLibraryInfoAddress ()
-{
- // punt on this for now
- return LLDB_INVALID_ADDRESS;
+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 ();
+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();
}
-bool
-NativeProcessLinux::GetArchitecture (ArchSpec &arch) const
-{
- arch = m_arch;
- return true;
+bool NativeProcessLinux::GetArchitecture(ArchSpec &arch) const {
+ arch = m_arch;
+ return true;
}
-Error
-NativeProcessLinux::GetSoftwareBreakpointPCOffset(uint32_t &actual_opcode_size)
-{
- // FIXME put this behind a breakpoint protocol class that can be
- // set per architecture. Need ARM, MIPS support here.
- static const uint8_t g_i386_opcode [] = { 0xCC };
- static const uint8_t g_s390x_opcode[] = { 0x00, 0x01 };
+Error NativeProcessLinux::GetSoftwareBreakpointPCOffset(
+ uint32_t &actual_opcode_size) {
+ // FIXME put this behind a breakpoint protocol class that can be
+ // set per architecture. Need ARM, MIPS support here.
+ static const uint8_t g_i386_opcode[] = {0xCC};
+ static const uint8_t g_s390x_opcode[] = {0x00, 0x01};
- switch (m_arch.GetMachine ())
- {
- case llvm::Triple::x86:
- case llvm::Triple::x86_64:
- actual_opcode_size = static_cast<uint32_t> (sizeof(g_i386_opcode));
- return Error ();
-
- case llvm::Triple::systemz:
- actual_opcode_size = static_cast<uint32_t> (sizeof(g_s390x_opcode));
- return Error ();
-
- case llvm::Triple::arm:
- case llvm::Triple::aarch64:
- case llvm::Triple::mips64:
- case llvm::Triple::mips64el:
- case llvm::Triple::mips:
- case llvm::Triple::mipsel:
- // On these architectures the PC don't get updated for breakpoint hits
- actual_opcode_size = 0;
- return Error ();
-
- default:
- assert(false && "CPU type not supported!");
- return Error ("CPU type not supported");
- }
-}
+ switch (m_arch.GetMachine()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ actual_opcode_size = static_cast<uint32_t>(sizeof(g_i386_opcode));
+ return Error();
-Error
-NativeProcessLinux::SetBreakpoint (lldb::addr_t addr, uint32_t size, bool hardware)
-{
- if (hardware)
- return Error ("NativeProcessLinux does not support hardware breakpoints");
- else
- return SetSoftwareBreakpoint (addr, size);
-}
+ case llvm::Triple::systemz:
+ actual_opcode_size = static_cast<uint32_t>(sizeof(g_s390x_opcode));
+ return Error();
-Error
-NativeProcessLinux::GetSoftwareBreakpointTrapOpcode (size_t trap_opcode_size_hint,
- size_t &actual_opcode_size,
- const uint8_t *&trap_opcode_bytes)
-{
- // FIXME put this behind a breakpoint protocol class that can be set per
- // architecture. Need MIPS support here.
- static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 };
- // 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_i386_opcode [] = { 0xCC };
- static const uint8_t g_mips64_opcode[] = { 0x00, 0x00, 0x00, 0x0d };
- static const uint8_t g_mips64el_opcode[] = { 0x0d, 0x00, 0x00, 0x00 };
- static const uint8_t g_s390x_opcode[] = { 0x00, 0x01 };
- static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde };
-
- switch (m_arch.GetMachine ())
- {
- case llvm::Triple::aarch64:
- trap_opcode_bytes = g_aarch64_opcode;
- actual_opcode_size = sizeof(g_aarch64_opcode);
- return Error ();
-
- case llvm::Triple::arm:
- switch (trap_opcode_size_hint)
- {
- case 2:
- trap_opcode_bytes = g_thumb_breakpoint_opcode;
- actual_opcode_size = sizeof(g_thumb_breakpoint_opcode);
- return Error ();
- case 4:
- trap_opcode_bytes = g_arm_breakpoint_opcode;
- actual_opcode_size = sizeof(g_arm_breakpoint_opcode);
- return Error ();
- default:
- assert(false && "Unrecognised trap opcode size hint!");
- return Error ("Unrecognised trap opcode size hint!");
- }
+ case llvm::Triple::arm:
+ case llvm::Triple::aarch64:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ // On these architectures the PC don't get updated for breakpoint hits
+ actual_opcode_size = 0;
+ return Error();
- case llvm::Triple::x86:
- case llvm::Triple::x86_64:
- trap_opcode_bytes = g_i386_opcode;
- actual_opcode_size = sizeof(g_i386_opcode);
- return Error ();
-
- case llvm::Triple::mips:
- case llvm::Triple::mips64:
- trap_opcode_bytes = g_mips64_opcode;
- actual_opcode_size = sizeof(g_mips64_opcode);
- return Error ();
-
- case llvm::Triple::mipsel:
- case llvm::Triple::mips64el:
- trap_opcode_bytes = g_mips64el_opcode;
- actual_opcode_size = sizeof(g_mips64el_opcode);
- return Error ();
-
- case llvm::Triple::systemz:
- trap_opcode_bytes = g_s390x_opcode;
- actual_opcode_size = sizeof(g_s390x_opcode);
- return Error ();
+ default:
+ assert(false && "CPU type not supported!");
+ return Error("CPU type not supported");
+ }
+}
+
+Error NativeProcessLinux::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ if (hardware)
+ return Error("NativeProcessLinux does not support hardware breakpoints");
+ else
+ return SetSoftwareBreakpoint(addr, size);
+}
+
+Error NativeProcessLinux::GetSoftwareBreakpointTrapOpcode(
+ size_t trap_opcode_size_hint, size_t &actual_opcode_size,
+ const uint8_t *&trap_opcode_bytes) {
+ // FIXME put this behind a breakpoint protocol class that can be set per
+ // architecture. Need MIPS support here.
+ static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4};
+ // 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_i386_opcode[] = {0xCC};
+ static const uint8_t g_mips64_opcode[] = {0x00, 0x00, 0x00, 0x0d};
+ static const uint8_t g_mips64el_opcode[] = {0x0d, 0x00, 0x00, 0x00};
+ static const uint8_t g_s390x_opcode[] = {0x00, 0x01};
+ static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde};
+
+ switch (m_arch.GetMachine()) {
+ case llvm::Triple::aarch64:
+ trap_opcode_bytes = g_aarch64_opcode;
+ actual_opcode_size = sizeof(g_aarch64_opcode);
+ return Error();
+ case llvm::Triple::arm:
+ switch (trap_opcode_size_hint) {
+ case 2:
+ trap_opcode_bytes = g_thumb_breakpoint_opcode;
+ actual_opcode_size = sizeof(g_thumb_breakpoint_opcode);
+ return Error();
+ case 4:
+ trap_opcode_bytes = g_arm_breakpoint_opcode;
+ actual_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ return Error();
default:
- assert(false && "CPU type not supported!");
- return Error ("CPU type not supported");
+ assert(false && "Unrecognised trap opcode size hint!");
+ return Error("Unrecognised trap opcode size hint!");
}
+
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ trap_opcode_bytes = g_i386_opcode;
+ actual_opcode_size = sizeof(g_i386_opcode);
+ return Error();
+
+ case llvm::Triple::mips:
+ case llvm::Triple::mips64:
+ trap_opcode_bytes = g_mips64_opcode;
+ actual_opcode_size = sizeof(g_mips64_opcode);
+ return Error();
+
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64el:
+ trap_opcode_bytes = g_mips64el_opcode;
+ actual_opcode_size = sizeof(g_mips64el_opcode);
+ return Error();
+
+ case llvm::Triple::systemz:
+ trap_opcode_bytes = g_s390x_opcode;
+ actual_opcode_size = sizeof(g_s390x_opcode);
+ return Error();
+
+ default:
+ assert(false && "CPU type not supported!");
+ return Error("CPU type not supported");
+ }
}
#if 0
@@ -2214,7 +2012,6 @@ NativeProcessLinux::GetCrashReasonForSIGSEGV(const siginfo_t *info)
}
#endif
-
#if 0
ProcessMessage::CrashReason
NativeProcessLinux::GetCrashReasonForSIGILL(const siginfo_t *info)
@@ -2332,654 +2129,628 @@ NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info)
}
#endif
-Error
-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.
+Error 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();
+ 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;
+ 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;
+ bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
+ const bool success = bytes_read == size;
- Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
- if (log)
- log->Printf ("NativeProcessLinux::%s using process_vm_readv to read %zd bytes from inferior address 0x%" PRIx64": %s",
- __FUNCTION__, size, addr, success ? "Success" : strerror(errno));
-
- if (success)
- return Error();
- // else
- // the call failed for some reason, let's retry the read using ptrace api.
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("NativeProcessLinux::%s using process_vm_readv to read %zd "
+ "bytes from inferior address 0x%" PRIx64 ": %s",
+ __FUNCTION__, size, addr,
+ success ? "Success" : strerror(errno));
+
+ if (success)
+ return Error();
+ // 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_ALL));
+ if (log)
+ ProcessPOSIXLog::IncNestLevel();
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ log->GetMask().Test(POSIX_LOG_MEMORY))
+ log->Printf("NativeProcessLinux::%s(%p, %p, %zd, _)", __FUNCTION__,
+ (void *)addr, buf, size);
+
+ for (bytes_read = 0; bytes_read < size; bytes_read += remainder) {
+ Error error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_PEEKDATA, GetID(), (void *)addr, nullptr, 0, &data);
+ if (error.Fail()) {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
}
- unsigned char *dst = static_cast<unsigned char*>(buf);
- size_t remainder;
- long data;
+ remainder = size - bytes_read;
+ remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
- Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
- if (log)
- ProcessPOSIXLog::IncNestLevel();
- if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
- log->Printf ("NativeProcessLinux::%s(%p, %p, %zd, _)", __FUNCTION__, (void*)addr, buf, size);
-
- for (bytes_read = 0; bytes_read < size; bytes_read += remainder)
- {
- Error error = NativeProcessLinux::PtraceWrapper(PTRACE_PEEKDATA, GetID(), (void*)addr, nullptr, 0, &data);
- if (error.Fail())
- {
- if (log)
- ProcessPOSIXLog::DecNestLevel();
- return error;
- }
+ // Copy the data into our buffer
+ memcpy(dst, &data, remainder);
- 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);
-
- if (log && ProcessPOSIXLog::AtTopNestLevel() &&
- (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
- (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
- size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
- {
- uintptr_t print_dst = 0;
- // Format bytes from data by moving into print_dst for log output
- for (unsigned i = 0; i < remainder; ++i)
- print_dst |= (((data >> i*8) & 0xFF) << i*8);
- log->Printf ("NativeProcessLinux::%s() [0x%" PRIx64 "]:0x%" PRIx64 " (0x%" PRIx64 ")",
- __FUNCTION__, addr, uint64_t(print_dst), uint64_t(data));
- }
- addr += k_ptrace_word_size;
- dst += k_ptrace_word_size;
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
+ size <= POSIX_LOG_MEMORY_SHORT_BYTES))) {
+ uintptr_t print_dst = 0;
+ // Format bytes from data by moving into print_dst for log output
+ for (unsigned i = 0; i < remainder; ++i)
+ print_dst |= (((data >> i * 8) & 0xFF) << i * 8);
+ log->Printf("NativeProcessLinux::%s() [0x%" PRIx64 "]:0x%" PRIx64
+ " (0x%" PRIx64 ")",
+ __FUNCTION__, addr, uint64_t(print_dst), uint64_t(data));
}
+ addr += k_ptrace_word_size;
+ dst += k_ptrace_word_size;
+ }
- if (log)
- ProcessPOSIXLog::DecNestLevel();
- return Error();
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return Error();
}
-Error
-NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read)
-{
- Error error = ReadMemory(addr, buf, size, bytes_read);
- if (error.Fail()) return error;
- return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size);
-}
-
-Error
-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;
- Error error;
+Error NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf,
+ size_t size,
+ size_t &bytes_read) {
+ Error error = ReadMemory(addr, buf, size, bytes_read);
+ if (error.Fail())
+ return error;
+ return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size);
+}
+
+Error 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;
+ Error error;
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_ALL));
+ if (log)
+ ProcessPOSIXLog::IncNestLevel();
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ log->GetMask().Test(POSIX_LOG_MEMORY))
+ log->Printf("NativeProcessLinux::%s(0x%" PRIx64 ", %p, %zu)", __FUNCTION__,
+ 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);
+
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
+ size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
+ log->Printf("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
+ (void *)addr, *(const unsigned long *)src, data);
+
+ error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(),
+ (void *)addr, (void *)data);
+ if (error.Fail()) {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
+ } else {
+ unsigned char buff[8];
+ size_t bytes_read;
+ error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read);
+ if (error.Fail()) {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
- Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_ALL));
- if (log)
- ProcessPOSIXLog::IncNestLevel();
- if (log && ProcessPOSIXLog::AtTopNestLevel() && log->GetMask().Test(POSIX_LOG_MEMORY))
- log->Printf ("NativeProcessLinux::%s(0x%" PRIx64 ", %p, %zu)", __FUNCTION__, addr, buf, size);
+ memcpy(buff, src, remainder);
- 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);
-
- if (log && ProcessPOSIXLog::AtTopNestLevel() &&
- (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
- (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
- size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
- log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
- (void*)addr, *(const unsigned long*)src, data);
-
- error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void*)addr, (void*)data);
- if (error.Fail())
- {
- if (log)
- ProcessPOSIXLog::DecNestLevel();
- return error;
- }
- }
- else
- {
- unsigned char buff[8];
- size_t bytes_read;
- error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read);
- if (error.Fail())
- {
- if (log)
- ProcessPOSIXLog::DecNestLevel();
- 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())
- {
- if (log)
- ProcessPOSIXLog::DecNestLevel();
- return error;
- }
-
- if (log && ProcessPOSIXLog::AtTopNestLevel() &&
- (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
- (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
- size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
- log->Printf ("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
- (void*)addr, *(const unsigned long*)src, *(unsigned long*)buff);
- }
+ size_t bytes_written_rec;
+ error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec);
+ if (error.Fail()) {
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
+ }
- addr += k_ptrace_word_size;
- src += k_ptrace_word_size;
+ if (log && ProcessPOSIXLog::AtTopNestLevel() &&
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_LONG) ||
+ (log->GetMask().Test(POSIX_LOG_MEMORY_DATA_SHORT) &&
+ size <= POSIX_LOG_MEMORY_SHORT_BYTES)))
+ log->Printf("NativeProcessLinux::%s() [%p]:0x%lx (0x%lx)", __FUNCTION__,
+ (void *)addr, *(const unsigned long *)src,
+ *(unsigned long *)buff);
}
- if (log)
- ProcessPOSIXLog::DecNestLevel();
- return error;
-}
-Error
-NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo)
-{
- return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo);
+ addr += k_ptrace_word_size;
+ src += k_ptrace_word_size;
+ }
+ if (log)
+ ProcessPOSIXLog::DecNestLevel();
+ return error;
}
-Error
-NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message)
-{
- return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message);
+Error NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) {
+ return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo);
}
-Error
-NativeProcessLinux::Detach(lldb::tid_t tid)
-{
- if (tid == LLDB_INVALID_THREAD_ID)
- return Error();
-
- return PtraceWrapper(PTRACE_DETACH, tid);
+Error NativeProcessLinux::GetEventMessage(lldb::tid_t tid,
+ unsigned long *message) {
+ return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message);
}
-bool
-NativeProcessLinux::DupDescriptor(const FileSpec &file_spec, int fd, int flags)
-{
- int target_fd = open(file_spec.GetCString(), flags, 0666);
-
- if (target_fd == -1)
- return false;
-
- if (dup2(target_fd, fd) == -1)
- return false;
+Error NativeProcessLinux::Detach(lldb::tid_t tid) {
+ if (tid == LLDB_INVALID_THREAD_ID)
+ return Error();
- return (close(target_fd) == -1) ? false : true;
+ return PtraceWrapper(PTRACE_DETACH, tid);
}
-bool
-NativeProcessLinux::HasThreadNoLock (lldb::tid_t thread_id)
-{
- for (auto thread_sp : m_threads)
- {
- assert (thread_sp && "thread list should not contain NULL threads");
- if (thread_sp->GetID () == thread_id)
- {
- // We have this thread.
- return true;
- }
+bool NativeProcessLinux::HasThreadNoLock(lldb::tid_t thread_id) {
+ for (auto thread_sp : m_threads) {
+ assert(thread_sp && "thread list should not contain NULL threads");
+ if (thread_sp->GetID() == thread_id) {
+ // We have this thread.
+ return true;
}
+ }
- // We don't have this thread.
- return false;
+ // We don't have this thread.
+ return false;
}
-bool
-NativeProcessLinux::StopTrackingThread (lldb::tid_t thread_id)
-{
- Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+bool NativeProcessLinux::StopTrackingThread(lldb::tid_t thread_id) {
+ Log *const log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD);
- if (log)
- log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, thread_id);
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__,
+ thread_id);
- bool found = false;
+ 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;
- }
+ for (auto it = m_threads.begin(); it != m_threads.end(); ++it) {
+ if (*it && ((*it)->GetID() == thread_id)) {
+ m_threads.erase(it);
+ found = true;
+ break;
}
+ }
- SignalIfAllThreadsStopped();
+ SignalIfAllThreadsStopped();
- return found;
+ return found;
}
-NativeThreadLinuxSP
-NativeProcessLinux::AddThread (lldb::tid_t thread_id)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+NativeThreadLinuxSP NativeProcessLinux::AddThread(lldb::tid_t thread_id) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
- if (log)
- {
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " adding thread with tid %" PRIu64,
- __FUNCTION__,
- GetID (),
- thread_id);
- }
+ if (log) {
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64
+ " adding thread with tid %" PRIu64,
+ __FUNCTION__, GetID(), thread_id);
+ }
- assert (!HasThreadNoLock (thread_id) && "attempted to add a thread by id that already exists");
+ 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);
+ // If this is the first thread, save it as the current thread
+ if (m_threads.empty())
+ SetCurrentThreadID(thread_id);
- auto thread_sp = std::make_shared<NativeThreadLinux>(this, thread_id);
- m_threads.push_back (thread_sp);
- return thread_sp;
+ auto thread_sp = std::make_shared<NativeThreadLinux>(this, thread_id);
+ m_threads.push_back(thread_sp);
+ return thread_sp;
}
-Error
-NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread)
-{
- Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+Error NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
- Error error;
+ Error error;
- // Find out the size of a breakpoint (might depend on where we are in the code).
- NativeRegisterContextSP context_sp = thread.GetRegisterContext();
- if (!context_sp)
- {
- error.SetErrorString ("cannot get a NativeRegisterContext for the thread");
- if (log)
- log->Printf ("NativeProcessLinux::%s failed: %s", __FUNCTION__, error.AsCString ());
- return error;
- }
-
- uint32_t breakpoint_size = 0;
- error = GetSoftwareBreakpointPCOffset(breakpoint_size);
- if (error.Fail ())
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s GetBreakpointSize() failed: %s", __FUNCTION__, error.AsCString ());
- return error;
- }
- else
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s breakpoint size: %" PRIu32, __FUNCTION__, breakpoint_size);
- }
-
- // First try probing for a breakpoint at a software breakpoint location: PC - breakpoint size.
- const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation ();
- lldb::addr_t breakpoint_addr = initial_pc_addr;
- if (breakpoint_size > 0)
- {
- // Do not allow breakpoint probe to wrap around.
- if (breakpoint_addr >= breakpoint_size)
- breakpoint_addr -= breakpoint_size;
- }
-
- // Check if we stopped because of a breakpoint.
- NativeBreakpointSP breakpoint_sp;
- error = m_breakpoint_list.GetBreakpoint (breakpoint_addr, breakpoint_sp);
- if (!error.Success () || !breakpoint_sp)
- {
- // We didn't find one at a software probe location. Nothing to do.
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " no lldb breakpoint found at current pc with adjustment: 0x%" PRIx64, __FUNCTION__, GetID (), breakpoint_addr);
- return Error ();
- }
-
- // If the breakpoint is not a software breakpoint, nothing to do.
- if (!breakpoint_sp->IsSoftwareBreakpoint ())
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", not software, nothing to adjust", __FUNCTION__, GetID (), breakpoint_addr);
- return Error ();
- }
-
- //
- // We have a software breakpoint and need to adjust the PC.
- //
-
- // Sanity check.
- if (breakpoint_size == 0)
- {
- // Nothing to do! How did we get here?
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " breakpoint found at 0x%" PRIx64 ", it is software, but the size is zero, nothing to do (unexpected)", __FUNCTION__, GetID (), breakpoint_addr);
- return Error ();
- }
-
- // Change the program counter.
+ // Find out the size of a breakpoint (might depend on where we are in the
+ // code).
+ NativeRegisterContextSP context_sp = thread.GetRegisterContext();
+ if (!context_sp) {
+ error.SetErrorString("cannot get a NativeRegisterContext for the thread");
if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": changing PC from 0x%" PRIx64 " to 0x%" PRIx64, __FUNCTION__, GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr);
-
- error = context_sp->SetPC (breakpoint_addr);
- if (error.Fail ())
- {
- if (log)
- log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64 ": failed to set PC: %s", __FUNCTION__, GetID(), thread.GetID(), error.AsCString ());
- return error;
- }
-
+ log->Printf("NativeProcessLinux::%s failed: %s", __FUNCTION__,
+ error.AsCString());
return error;
-}
-
-Error
-NativeProcessLinux::GetLoadedModuleFileSpec(const char* module_path, FileSpec& file_spec)
-{
- FileSpec module_file_spec(module_path, true);
-
- bool found = false;
- file_spec.Clear();
- ProcFileReader::ProcessLineByLine(GetID(), "maps",
- [&] (const std::string &line)
- {
- SmallVector<StringRef, 16> columns;
- StringRef(line).split(columns, " ", -1, false);
- if (columns.size() < 6)
- return true; // continue searching
-
- FileSpec this_file_spec(columns[5].str().c_str(), false);
- if (this_file_spec.GetFilename() != module_file_spec.GetFilename())
- return true; // continue searching
-
- file_spec = this_file_spec;
- found = true;
- return false; // we are done
- });
+ }
- if (! found)
- return Error("Module file (%s) not found in /proc/%" PRIu64 "/maps file!",
- module_file_spec.GetFilename().AsCString(), GetID());
+ uint32_t breakpoint_size = 0;
+ error = GetSoftwareBreakpointPCOffset(breakpoint_size);
+ if (error.Fail()) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s GetBreakpointSize() failed: %s",
+ __FUNCTION__, error.AsCString());
+ return error;
+ } else {
+ if (log)
+ log->Printf("NativeProcessLinux::%s breakpoint size: %" PRIu32,
+ __FUNCTION__, breakpoint_size);
+ }
+
+ // First try probing for a breakpoint at a software breakpoint location: PC -
+ // breakpoint size.
+ const lldb::addr_t initial_pc_addr =
+ context_sp->GetPCfromBreakpointLocation();
+ lldb::addr_t breakpoint_addr = initial_pc_addr;
+ if (breakpoint_size > 0) {
+ // Do not allow breakpoint probe to wrap around.
+ if (breakpoint_addr >= breakpoint_size)
+ breakpoint_addr -= breakpoint_size;
+ }
+
+ // Check if we stopped because of a breakpoint.
+ NativeBreakpointSP breakpoint_sp;
+ error = m_breakpoint_list.GetBreakpoint(breakpoint_addr, breakpoint_sp);
+ if (!error.Success() || !breakpoint_sp) {
+ // We didn't find one at a software probe location. Nothing to do.
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s pid %" PRIu64
+ " no lldb breakpoint found at current pc with adjustment: 0x%" PRIx64,
+ __FUNCTION__, GetID(), breakpoint_addr);
+ return Error();
+ }
+ // If the breakpoint is not a software breakpoint, nothing to do.
+ if (!breakpoint_sp->IsSoftwareBreakpoint()) {
+ if (log)
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64
+ " breakpoint found at 0x%" PRIx64
+ ", not software, nothing to adjust",
+ __FUNCTION__, GetID(), breakpoint_addr);
return Error();
-}
+ }
-Error
-NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef& file_name, lldb::addr_t& load_addr)
-{
- load_addr = LLDB_INVALID_ADDRESS;
- Error error = ProcFileReader::ProcessLineByLine (GetID (), "maps",
- [&] (const std::string &line) -> bool
- {
- StringRef maps_row(line);
-
- SmallVector<StringRef, 16> maps_columns;
- maps_row.split(maps_columns, StringRef(" "), -1, false);
-
- if (maps_columns.size() < 6)
- {
- // Return true to continue reading the proc file
- return true;
- }
-
- if (maps_columns[5] == file_name)
- {
- StringExtractor addr_extractor(maps_columns[0].str().c_str());
- load_addr = addr_extractor.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
-
- // Return false to stop reading the proc file further
- return false;
- }
-
- // Return true to continue reading the proc file
- return true;
- });
- return error;
-}
+ //
+ // We have a software breakpoint and need to adjust the PC.
+ //
-NativeThreadLinuxSP
-NativeProcessLinux::GetThreadByID(lldb::tid_t tid)
-{
- return std::static_pointer_cast<NativeThreadLinux>(NativeProcessProtocol::GetThreadByID(tid));
-}
+ // Sanity check.
+ if (breakpoint_size == 0) {
+ // Nothing to do! How did we get here?
+ if (log)
+ log->Printf(
+ "NativeProcessLinux::%s pid %" PRIu64
+ " breakpoint found at 0x%" PRIx64
+ ", it is software, but the size is zero, nothing to do (unexpected)",
+ __FUNCTION__, GetID(), breakpoint_addr);
+ return Error();
+ }
-Error
-NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo)
-{
- Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+ // Change the program counter.
+ if (log)
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64
+ ": changing PC from 0x%" PRIx64 " to 0x%" PRIx64,
+ __FUNCTION__, GetID(), thread.GetID(), initial_pc_addr,
+ breakpoint_addr);
+ error = context_sp->SetPC(breakpoint_addr);
+ if (error.Fail()) {
if (log)
- log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")",
- __FUNCTION__, 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 && log)
- {
- log->Printf("NativeProcessLinux::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, thread.GetID(), m_pending_notification_tid);
- }
+ log->Printf("NativeProcessLinux::%s pid %" PRIu64 " tid %" PRIu64
+ ": failed to set PC: %s",
+ __FUNCTION__, GetID(), thread.GetID(), error.AsCString());
+ return error;
+ }
- // 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:
- if (log)
- log->Printf("NativeProcessLinux::%s Unhandled state %s.",
- __FUNCTION__, StateAsCString(state));
- llvm_unreachable("Unhandled state for resume");
- }
+ return error;
}
-//===----------------------------------------------------------------------===//
+Error NativeProcessLinux::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ FileSpec module_file_spec(module_path, true);
-void
-NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid)
-{
- Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+ bool found = false;
+ file_spec.Clear();
+ ProcFileReader::ProcessLineByLine(
+ GetID(), "maps", [&](const std::string &line) {
+ SmallVector<StringRef, 16> columns;
+ StringRef(line).split(columns, " ", -1, false);
+ if (columns.size() < 6)
+ return true; // continue searching
- if (log)
- {
- log->Printf("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ")",
- __FUNCTION__, triggering_tid);
- }
+ FileSpec this_file_spec(columns[5].str(), false);
+ if (this_file_spec.GetFilename() != module_file_spec.GetFilename())
+ return true; // continue searching
- m_pending_notification_tid = triggering_tid;
+ file_spec = this_file_spec;
+ found = true;
+ return false; // we are done
+ });
- // 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_sp: m_threads)
- {
- if (StateIsRunningState(thread_sp->GetState()))
- static_pointer_cast<NativeThreadLinux>(thread_sp)->RequestStop();
- }
-
- SignalIfAllThreadsStopped();
+ if (!found)
+ return Error("Module file (%s) not found in /proc/%" PRIu64 "/maps file!",
+ module_file_spec.GetFilename().AsCString(), GetID());
- if (log)
- {
- log->Printf("NativeProcessLinux::%s event processing done", __FUNCTION__);
- }
+ return Error();
}
-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.
- }
+Error NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ load_addr = LLDB_INVALID_ADDRESS;
+ Error error = ProcFileReader::ProcessLineByLine(
+ GetID(), "maps", [&](const std::string &line) -> bool {
+ StringRef maps_row(line);
- // We have a pending notification and all threads have stopped.
- Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
+ SmallVector<StringRef, 16> maps_columns;
+ maps_row.split(maps_columns, StringRef(" "), -1, false);
- // Clear any temporary breakpoints we used to implement software single stepping.
- for (const auto &thread_info: m_threads_stepping_with_breakpoint)
- {
- Error error = RemoveBreakpoint (thread_info.second);
- if (error.Fail())
- if (log)
- log->Printf("NativeProcessLinux::%s() pid = %" PRIu64 " remove stepping breakpoint: %s",
- __FUNCTION__, thread_info.first, error.AsCString());
- }
- m_threads_stepping_with_breakpoint.clear();
+ if (maps_columns.size() < 6) {
+ // Return true to continue reading the proc file
+ return true;
+ }
- // Notify the delegate about the stop
- SetCurrentThreadID(m_pending_notification_tid);
- SetState(StateType::eStateStopped, true);
- m_pending_notification_tid = LLDB_INVALID_THREAD_ID;
-}
+ if (maps_columns[5] == file_name) {
+ StringExtractor addr_extractor(maps_columns[0].str().c_str());
+ load_addr = addr_extractor.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
-void
-NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread)
-{
- Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
+ // Return false to stop reading the proc file further
+ return false;
+ }
+ // Return true to continue reading the proc file
+ return true;
+ });
+ return error;
+}
+
+NativeThreadLinuxSP NativeProcessLinux::GetThreadByID(lldb::tid_t tid) {
+ return std::static_pointer_cast<NativeThreadLinux>(
+ NativeProcessProtocol::GetThreadByID(tid));
+}
+
+Error NativeProcessLinux::ResumeThread(NativeThreadLinux &thread,
+ lldb::StateType state, int signo) {
+ Log *const log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD);
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__,
+ 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 && log) {
+ log->Printf("NativeProcessLinux::%s about to resume tid %" PRIu64
+ " per explicit request but we have a pending stop notification "
+ "(tid %" PRIu64 ") that is actively waiting for this thread to "
+ "stop. Valid sequence of events?",
+ __FUNCTION__, 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:
if (log)
- log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__, 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();
- }
+ log->Printf("NativeProcessLinux::%s Unhandled state %s.", __FUNCTION__,
+ StateAsCString(state));
+ llvm_unreachable("Unhandled state for resume");
+ }
}
-void
-NativeProcessLinux::SigchldHandler()
-{
- Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
- // Process all pending waitpid notifications.
- while (true)
- {
- int status = -1;
- ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG);
+//===----------------------------------------------------------------------===//
- if (wait_pid == 0)
- break; // We are done.
+void NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid) {
+ Log *const log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD);
- if (wait_pid == -1)
- {
- if (errno == EINTR)
- continue;
+ if (log) {
+ log->Printf("NativeProcessLinux::%s about to process event: "
+ "(triggering_tid: %" PRIu64 ")",
+ __FUNCTION__, triggering_tid);
+ }
- Error error(errno, eErrorTypePOSIX);
- if (log)
- log->Printf("NativeProcessLinux::%s waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG) failed: %s",
- __FUNCTION__, error.AsCString());
- break;
- }
+ m_pending_notification_tid = triggering_tid;
- bool exited = false;
- int signal = 0;
- int exit_status = 0;
- const char *status_cstr = nullptr;
- if (WIFSTOPPED(status))
- {
- signal = WSTOPSIG(status);
- status_cstr = "STOPPED";
- }
- else if (WIFEXITED(status))
- {
- exit_status = WEXITSTATUS(status);
- status_cstr = "EXITED";
- exited = true;
- }
- else if (WIFSIGNALED(status))
- {
- signal = WTERMSIG(status);
- status_cstr = "SIGNALED";
- if (wait_pid == static_cast< ::pid_t>(GetID())) {
- exited = true;
- exit_status = -1;
- }
- }
- else
- status_cstr = "(\?\?\?)";
+ // 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_sp : m_threads) {
+ if (StateIsRunningState(thread_sp->GetState()))
+ static_pointer_cast<NativeThreadLinux>(thread_sp)->RequestStop();
+ }
- if (log)
- log->Printf("NativeProcessLinux::%s: waitpid (-1, &status, __WALL | __WNOTHREAD | WNOHANG)"
- "=> pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
- __FUNCTION__, wait_pid, status, status_cstr, signal, exit_status);
+ SignalIfAllThreadsStopped();
- MonitorCallback (wait_pid, exited, signal, exit_status);
- }
+ if (log) {
+ log->Printf("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
}
-// 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*)
-Error
-NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, long *result)
-{
- Error error;
- long int ret;
-
- Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PTRACE));
-
- PtraceDisplayBytes(req, data, data_size);
+void NativeProcessLinux::SignalIfAllThreadsStopped() {
+ if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID)
+ return; // No pending notification. Nothing to do.
- 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);
+ for (const auto &thread_sp : m_threads) {
+ if (StateIsRunningState(thread_sp->GetState()))
+ return; // Some threads are still running. Don't signal yet.
+ }
- if (ret == -1)
- error.SetErrorToErrno();
+ // We have a pending notification and all threads have stopped.
+ Log *log(
+ GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS));
- if (result)
- *result = ret;
+ // Clear any temporary breakpoints we used to implement software single
+ // stepping.
+ for (const auto &thread_info : m_threads_stepping_with_breakpoint) {
+ Error error = RemoveBreakpoint(thread_info.second);
+ if (error.Fail())
+ if (log)
+ log->Printf("NativeProcessLinux::%s() pid = %" PRIu64
+ " remove stepping breakpoint: %s",
+ __FUNCTION__, thread_info.first, error.AsCString());
+ }
+ 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 = GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD);
+
+ if (log)
+ log->Printf("NativeProcessLinux::%s (tid: %" PRIu64 ")", __FUNCTION__,
+ 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(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ // Process all pending waitpid notifications.
+ while (true) {
+ int status = -1;
+ ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG);
+
+ if (wait_pid == 0)
+ break; // We are done.
+
+ if (wait_pid == -1) {
+ if (errno == EINTR)
+ continue;
+
+ Error error(errno, eErrorTypePOSIX);
+ if (log)
+ log->Printf("NativeProcessLinux::%s waitpid (-1, &status, __WALL | "
+ "__WNOTHREAD | WNOHANG) failed: %s",
+ __FUNCTION__, error.AsCString());
+ break;
+ }
+
+ bool exited = false;
+ int signal = 0;
+ int exit_status = 0;
+ const char *status_cstr = nullptr;
+ if (WIFSTOPPED(status)) {
+ signal = WSTOPSIG(status);
+ status_cstr = "STOPPED";
+ } else if (WIFEXITED(status)) {
+ exit_status = WEXITSTATUS(status);
+ status_cstr = "EXITED";
+ exited = true;
+ } else if (WIFSIGNALED(status)) {
+ signal = WTERMSIG(status);
+ status_cstr = "SIGNALED";
+ if (wait_pid == static_cast<::pid_t>(GetID())) {
+ exited = true;
+ exit_status = -1;
+ }
+ } else
+ status_cstr = "(\?\?\?)";
if (log)
- log->Printf("ptrace(%d, %" PRIu64 ", %p, %p, %zu)=%lX", req, pid, addr, data, data_size, ret);
+ log->Printf("NativeProcessLinux::%s: waitpid (-1, &status, __WALL | "
+ "__WNOTHREAD | WNOHANG)"
+ "=> pid = %" PRIi32
+ ", status = 0x%8.8x (%s), signal = %i, exit_state = %i",
+ __FUNCTION__, wait_pid, status, status_cstr, signal,
+ exit_status);
- PtraceDisplayBytes(req, data, data_size);
+ MonitorCallback(wait_pid, exited, signal, exit_status);
+ }
+}
- if (log && error.GetError() != 0)
- {
- const char* str;
- switch (error.GetError())
- {
- case ESRCH: str = "ESRCH"; break;
- case EINVAL: str = "EINVAL"; break;
- case EBUSY: str = "EBUSY"; break;
- case EPERM: str = "EPERM"; break;
- default: str = error.AsCString();
- }
- log->Printf("ptrace() failed; errno=%d (%s)", error.GetError(), str);
+// 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*)
+Error NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
+ void *data, size_t data_size,
+ long *result) {
+ Error 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;
+
+ if (log)
+ log->Printf("ptrace(%d, %" PRIu64 ", %p, %p, %zu)=%lX", req, pid, addr,
+ data, data_size, ret);
+
+ PtraceDisplayBytes(req, data, data_size);
+
+ if (log && error.GetError() != 0) {
+ const char *str;
+ switch (error.GetError()) {
+ case ESRCH:
+ str = "ESRCH";
+ break;
+ case EINVAL:
+ str = "EINVAL";
+ break;
+ case EBUSY:
+ str = "EBUSY";
+ break;
+ case EPERM:
+ str = "EPERM";
+ break;
+ default:
+ str = error.AsCString();
}
+ log->Printf("ptrace() failed; errno=%d (%s)", error.GetError(), str);
+ }
- return error;
+ return error;
}