diff options
Diffstat (limited to 'source/Plugins/Process')
93 files changed, 13358 insertions, 7012 deletions
diff --git a/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp b/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp index 493f36ca8b48..cce7a1ef28c1 100644 --- a/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp +++ b/source/Plugins/Process/FreeBSD/FreeBSDThread.cpp @@ -11,6 +11,7 @@ // C++ Includes // Other libraries and framework includes #include "lldb/Core/State.h" +#include "lldb/Target/UnixSignals.h" // Project includes #include "FreeBSDThread.h" diff --git a/source/Plugins/Process/POSIX/POSIXStopInfo.cpp b/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp index 3b8cea737bcb..3b8cea737bcb 100644 --- a/source/Plugins/Process/POSIX/POSIXStopInfo.cpp +++ b/source/Plugins/Process/FreeBSD/POSIXStopInfo.cpp diff --git a/source/Plugins/Process/POSIX/POSIXStopInfo.h b/source/Plugins/Process/FreeBSD/POSIXStopInfo.h index a1ee2ea68524..a1ee2ea68524 100644 --- a/source/Plugins/Process/POSIX/POSIXStopInfo.h +++ b/source/Plugins/Process/FreeBSD/POSIXStopInfo.h diff --git a/source/Plugins/Process/POSIX/POSIXThread.cpp b/source/Plugins/Process/FreeBSD/POSIXThread.cpp index 1057585e1b2a..854796fb7448 100644 --- a/source/Plugins/Process/POSIX/POSIXThread.cpp +++ b/source/Plugins/Process/FreeBSD/POSIXThread.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - // C Includes #include <errno.h> @@ -31,14 +29,13 @@ #include "POSIXThread.h" #include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" -#include "Plugins/Process/Linux/ProcessMonitor.h" +#include "ProcessMonitor.h" +#include "RegisterContextPOSIXProcessMonitor_arm.h" #include "RegisterContextPOSIXProcessMonitor_arm64.h" #include "RegisterContextPOSIXProcessMonitor_mips64.h" #include "RegisterContextPOSIXProcessMonitor_powerpc.h" #include "RegisterContextPOSIXProcessMonitor_x86.h" -#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" -#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" -#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_arm.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" @@ -98,7 +95,6 @@ POSIXThread::GetMonitor() return process.GetMonitor(); } -// Overridden by FreeBSDThread; this is used only on Linux. void POSIXThread::RefreshStateAfterStop() { @@ -115,11 +111,6 @@ POSIXThread::RefreshStateAfterStop() const bool force = false; GetRegisterContext()->InvalidateIfNeeded (force); } - // FIXME: This should probably happen somewhere else. - SetResumeState(eStateRunning, true); - Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); - if (log) - log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to running", __FUNCTION__, GetID()); } const char * @@ -169,6 +160,9 @@ POSIXThread::GetRegisterContext() case llvm::Triple::FreeBSD: switch (target_arch.GetMachine()) { + case llvm::Triple::arm: + reg_interface = new RegisterContextFreeBSD_arm(target_arch); + break; case llvm::Triple::ppc: #ifndef __powerpc64__ reg_interface = new RegisterContextFreeBSD_powerpc32(target_arch); @@ -191,32 +185,6 @@ POSIXThread::GetRegisterContext() } break; - case llvm::Triple::Linux: - switch (target_arch.GetMachine()) - { - case llvm::Triple::aarch64: - assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && "Register setting path assumes this is a 64-bit host"); - reg_interface = static_cast<RegisterInfoInterface*>(new RegisterContextLinux_arm64(target_arch)); - break; - case llvm::Triple::x86: - case llvm::Triple::x86_64: - if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) - { - // 32-bit hosts run with a RegisterContextLinux_i386 context. - reg_interface = static_cast<RegisterInfoInterface*>(new RegisterContextLinux_i386(target_arch)); - } - else - { - assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && - "Register setting path assumes this is a 64-bit host"); - // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the x86_64 register context. - reg_interface = static_cast<RegisterInfoInterface*>(new RegisterContextLinux_x86_64(target_arch)); - } - break; - default: - break; - } - default: break; } @@ -232,6 +200,13 @@ POSIXThread::GetRegisterContext() m_reg_context_sp.reset(reg_ctx); break; } + case llvm::Triple::arm: + { + RegisterContextPOSIXProcessMonitor_arm *reg_ctx = new RegisterContextPOSIXProcessMonitor_arm(*this, 0, reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } case llvm::Triple::mips64: { RegisterContextPOSIXProcessMonitor_mips64 *reg_ctx = new RegisterContextPOSIXProcessMonitor_mips64(*this, 0, reg_interface); @@ -313,18 +288,6 @@ POSIXThread::GetUnwinder() return m_unwinder_ap.get(); } -// Overridden by FreeBSDThread; this is used only on Linux. -void -POSIXThread::WillResume(lldb::StateType resume_state) -{ - Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); - if (log) - log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to %s", __FUNCTION__, GetID(), StateAsCString(resume_state)); - // TODO: the line below shouldn't really be done, but - // the POSIXThread might rely on this so I will leave this in for now - SetResumeState(resume_state); -} - void POSIXThread::DidStop() { @@ -512,7 +475,10 @@ POSIXThread::BreakNotify(const ProcessMessage &message) if (bp_site) { lldb::break_id_t bp_id = bp_site->GetID(); - if (bp_site->ValidForThisThread(this)) + // If we have an operating system plug-in, we might have set a thread specific breakpoint using the + // operating system thread ID, so we can't make any assumptions about the thread ID so we must always + // report the breakpoint regardless of the thread. + if (bp_site->ValidForThisThread(this) || GetProcess()->GetOperatingSystem () != NULL) SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id)); else { @@ -641,6 +607,7 @@ POSIXThread::GetRegisterIndexFromOffset(unsigned offset) break; case llvm::Triple::aarch64: + case llvm::Triple::arm: case llvm::Triple::mips64: case llvm::Triple::ppc: case llvm::Triple::ppc64: @@ -674,6 +641,7 @@ POSIXThread::GetRegisterName(unsigned reg) break; case llvm::Triple::aarch64: + case llvm::Triple::arm: case llvm::Triple::mips64: case llvm::Triple::ppc: case llvm::Triple::ppc64: diff --git a/source/Plugins/Process/POSIX/POSIXThread.h b/source/Plugins/Process/FreeBSD/POSIXThread.h index 56dcccbfb0f9..c38d194dbd19 100644 --- a/source/Plugins/Process/POSIX/POSIXThread.h +++ b/source/Plugins/Process/FreeBSD/POSIXThread.h @@ -35,32 +35,29 @@ public: virtual ~POSIXThread(); void - RefreshStateAfterStop(); - - virtual void - WillResume(lldb::StateType resume_state); + RefreshStateAfterStop() override; // This notifies the thread when a private stop occurs. - virtual void - DidStop (); + void + DidStop () override; const char * - GetInfo(); + GetInfo() override; void - SetName (const char *name); + SetName (const char *name) override; const char * - GetName (); + GetName () override; - virtual lldb::RegisterContextSP - GetRegisterContext(); + lldb::RegisterContextSP + GetRegisterContext() override; - virtual lldb::RegisterContextSP - CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame) override; - virtual lldb::addr_t - GetThreadPointer (); + lldb::addr_t + GetThreadPointer () override; //-------------------------------------------------------------------------- // These functions provide a mapping from the register offset @@ -114,8 +111,8 @@ protected: ProcessMonitor & GetMonitor(); - virtual bool - CalculateStopInfo(); + bool + CalculateStopInfo() override; void BreakNotify(const ProcessMessage &message); void WatchNotify(const ProcessMessage &message); @@ -129,7 +126,7 @@ protected: void ExecNotify(const ProcessMessage &message); lldb_private::Unwind * - GetUnwinder(); + GetUnwinder() override; }; #endif // #ifndef liblldb_POSIXThread_H_ diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp index 5a0b5ed14194..a0458f16e558 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -11,6 +11,8 @@ #include <errno.h> // C++ Includes +#include <mutex> + // Other libraries and framework includes #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" @@ -56,23 +58,14 @@ ProcessFreeBSD::CreateInstance(Target& target, void ProcessFreeBSD::Initialize() { - static bool g_initialized = false; + static std::once_flag g_once_flag; - if (!g_initialized) - { + std::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); - Log::Callbacks log_callbacks = { - ProcessPOSIXLog::DisableLog, - ProcessPOSIXLog::EnableLog, - ProcessPOSIXLog::ListLogCategories - }; - - Log::RegisterLogChannel (ProcessFreeBSD::GetPluginNameStatic(), log_callbacks); - ProcessPOSIXLog::RegisterPluginName(GetPluginNameStatic()); - g_initialized = true; - } + ProcessPOSIXLog::Initialize(GetPluginNameStatic()); + }); } lldb_private::ConstString diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp index b33f83303971..28bca0916148 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -28,6 +28,7 @@ #include "lldb/Host/ThreadLauncher.h" #include "lldb/Target/Thread.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/UnixSignals.h" #include "lldb/Utility/PseudoTerminal.h" #include "Plugins/Process/POSIX/CrashReason.h" @@ -107,13 +108,9 @@ PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, if (req == PT_GETREGS) { struct reg *r = (struct reg *) addr; - log->Printf("PT_GETREGS: ip=0x%lx", r->r_rip); - log->Printf("PT_GETREGS: sp=0x%lx", r->r_rsp); - log->Printf("PT_GETREGS: bp=0x%lx", r->r_rbp); - log->Printf("PT_GETREGS: ax=0x%lx", r->r_rax); + log->Printf("PT_GETREGS: rip=0x%lx rsp=0x%lx rbp=0x%lx rax=0x%lx", + r->r_rip, r->r_rsp, r->r_rbp, r->r_rax); } -#endif -#ifndef __powerpc__ if (req == PT_GETDBREGS || req == PT_SETDBREGS) { struct dbreg *r = (struct dbreg *) addr; char setget = (req == PT_GETDBREGS) ? 'G' : 'S'; @@ -772,17 +769,17 @@ ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module, char const **argv, char const **envp, - const char *stdin_path, - const char *stdout_path, - const char *stderr_path, - const char *working_dir) + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir) : OperationArgs(monitor), m_module(module), m_argv(argv), m_envp(envp), - m_stdin_path(stdin_path), - m_stdout_path(stdout_path), - m_stderr_path(stderr_path), + m_stdin_file_spec(stdin_file_spec), + m_stdout_file_spec(stdout_file_spec), + m_stderr_file_spec(stderr_file_spec), m_working_dir(working_dir) { } ProcessMonitor::LaunchArgs::~LaunchArgs() @@ -811,10 +808,10 @@ ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, Module *module, const char *argv[], const char *envp[], - const char *stdin_path, - const char *stdout_path, - const char *stderr_path, - const char *working_dir, + const FileSpec &stdin_file_spec, + const FileSpec &stdout_file_spec, + const FileSpec &stderr_file_spec, + const FileSpec &working_dir, const lldb_private::ProcessLaunchInfo & /* launch_info */, lldb_private::Error &error) : m_process(static_cast<ProcessFreeBSD *>(process)), @@ -823,8 +820,10 @@ ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, m_operation(0) { std::unique_ptr<LaunchArgs> args(new LaunchArgs(this, module, argv, envp, - stdin_path, stdout_path, stderr_path, - working_dir)); + stdin_file_spec, + stdout_file_spec, + stderr_file_spec, + working_dir)); sem_init(&m_operation_pending, 0, 0); @@ -955,15 +954,15 @@ ProcessMonitor::Launch(LaunchArgs *args) ProcessFreeBSD &process = monitor->GetProcess(); const char **argv = args->m_argv; const char **envp = args->m_envp; - const char *stdin_path = args->m_stdin_path; - const char *stdout_path = args->m_stdout_path; - const char *stderr_path = args->m_stderr_path; - const char *working_dir = args->m_working_dir; + const FileSpec &stdin_file_spec = args->m_stdin_file_spec; + const FileSpec &stdout_file_spec = args->m_stdout_file_spec; + const FileSpec &stderr_file_spec = args->m_stderr_file_spec; + const FileSpec &working_dir = args->m_working_dir; lldb_utility::PseudoTerminal terminal; const size_t err_len = 1024; char err_str[err_len]; - lldb::pid_t pid; + ::pid_t pid; // Propagate the environment if one is not supplied. if (envp == NULL || envp[0] == NULL) @@ -1010,22 +1009,21 @@ ProcessMonitor::Launch(LaunchArgs *args) // // FIXME: If two or more of the paths are the same we needlessly open // the same file multiple times. - if (stdin_path != NULL && stdin_path[0]) - if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY)) + if (stdin_file_spec) + if (!DupDescriptor(stdin_file_spec, STDIN_FILENO, O_RDONLY)) exit(eDupStdinFailed); - if (stdout_path != NULL && stdout_path[0]) - if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT)) + if (stdout_file_spec) + if (!DupDescriptor(stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT)) exit(eDupStdoutFailed); - if (stderr_path != NULL && stderr_path[0]) - if (!DupDescriptor(stderr_path, STDERR_FILENO, O_WRONLY | O_CREAT)) + if (stderr_file_spec) + if (!DupDescriptor(stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT)) exit(eDupStderrFailed); // Change working directory - if (working_dir != NULL && working_dir[0]) - if (0 != ::chdir(working_dir)) - exit(eChdirFailed); + if (working_dir && 0 != ::chdir(working_dir.GetCString())) + exit(eChdirFailed); // Execute. We should never return. execve(argv[0], @@ -1556,9 +1554,9 @@ ProcessMonitor::Detach(lldb::tid_t tid) } bool -ProcessMonitor::DupDescriptor(const char *path, int fd, int flags) +ProcessMonitor::DupDescriptor(const FileSpec &file_spec, int fd, int flags) { - int target_fd = open(path, flags, 0666); + int target_fd = open(file_spec.GetCString(), flags, 0666); if (target_fd == -1) return false; diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/source/Plugins/Process/FreeBSD/ProcessMonitor.h index 4ae963c89a2f..20ce582d973e 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.h +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -17,6 +17,7 @@ // C++ Includes // Other libraries and framework includes #include "lldb/lldb-types.h" +#include "lldb/Host/FileSpec.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/Mutex.h" @@ -52,10 +53,10 @@ public: lldb_private::Module *module, char const *argv[], char const *envp[], - const char *stdin_path, - const char *stdout_path, - const char *stderr_path, - const char *working_dir, + const lldb_private::FileSpec &stdin_file_spec, + const lldb_private::FileSpec &stdout_file_spec, + const lldb_private::FileSpec &stderr_file_spec, + const lldb_private::FileSpec &working_dir, const lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Error &error); @@ -228,7 +229,7 @@ private: // the operation is complete. sem_t m_operation_pending; sem_t m_operation_done; - + struct OperationArgs { OperationArgs(ProcessMonitor *monitor); @@ -250,20 +251,20 @@ private: lldb_private::Module *module, char const **argv, char const **envp, - const char *stdin_path, - const char *stdout_path, - const char *stderr_path, - const char *working_dir); + const lldb_private::FileSpec &stdin_file_spec, + const lldb_private::FileSpec &stdout_file_spec, + const lldb_private::FileSpec &stderr_file_spec, + const lldb_private::FileSpec &working_dir); ~LaunchArgs(); - lldb_private::Module *m_module; // The executable image to launch. - char const **m_argv; // Process arguments. - char const **m_envp; // Process environment. - const char *m_stdin_path; // Redirect stdin or NULL. - const char *m_stdout_path; // Redirect stdout or NULL. - const char *m_stderr_path; // Redirect stderr or NULL. - const char *m_working_dir; // Working directory or NULL. + lldb_private::Module *m_module; // The executable image to launch. + char const **m_argv; // Process arguments. + char const **m_envp; // Process environment. + const lldb_private::FileSpec m_stdin_file_spec; // Redirect stdin or empty. + const lldb_private::FileSpec m_stdout_file_spec; // Redirect stdout or empty. + const lldb_private::FileSpec m_stderr_file_spec; // Redirect stderr or empty. + const lldb_private::FileSpec m_working_dir; // Working directory or empty. }; void @@ -298,7 +299,7 @@ private: ServeOperation(OperationArgs *args); static bool - DupDescriptor(const char *path, int fd, int flags); + DupDescriptor(const lldb_private::FileSpec &file_spec, int fd, int flags); static bool MonitorCallback(void *callback_baton, diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/source/Plugins/Process/FreeBSD/ProcessPOSIX.cpp index 882fac75c9a0..360382e2e63a 100644 --- a/source/Plugins/Process/POSIX/ProcessPOSIX.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessPOSIX.cpp @@ -7,13 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - // C Includes #include <errno.h> // C++ Includes // Other libraries and framework includes +#include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" @@ -29,46 +28,15 @@ #include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" -#include "Plugins/Process/Linux/ProcessMonitor.h" #include "POSIXThread.h" +#include "ProcessMonitor.h" + +#include "lldb/Host/posix/Fcntl.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------------ -// Static functions. -#if 0 -Process* -ProcessPOSIX::CreateInstance(Target& target, Listener &listener) -{ - return new ProcessPOSIX(target, listener); -} - - -void -ProcessPOSIX::Initialize() -{ - static bool g_initialized = false; - - if (!g_initialized) - { - g_initialized = true; - PluginManager::RegisterPlugin(GetPluginNameStatic(), - GetPluginDescriptionStatic(), - CreateInstance); - - Log::Callbacks log_callbacks = { - ProcessPOSIXLog::DisableLog, - ProcessPOSIXLog::EnableLog, - ProcessPOSIXLog::ListLogCategories - }; - - Log::RegisterLogChannel (ProcessPOSIX::GetPluginNameStatic(), log_callbacks); - } -} -#endif - -//------------------------------------------------------------------------------ // Constructors and destructors. ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener, UnixSignalsSP &unix_signals_sp) @@ -82,9 +50,9 @@ ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener, UnixSignalsSP &un { // FIXME: Putting this code in the ctor and saving the byte order in a // member variable is a hack to avoid const qual issues in GetByteOrder. - lldb::ModuleSP module = GetTarget().GetExecutableModule(); - if (module && module->GetObjectFile()) - m_byte_order = module->GetObjectFile()->GetByteOrder(); + lldb::ModuleSP module = GetTarget().GetExecutableModule(); + if (module && module->GetObjectFile()) + m_byte_order = module->GetObjectFile()->GetByteOrder(); } ProcessPOSIX::~ProcessPOSIX() @@ -115,7 +83,7 @@ ProcessPOSIX::CanDebug(Target &target, bool plugin_specified_by_name) } Error -ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid) +ProcessPOSIX::DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) { Error error; assert(m_monitor == NULL); @@ -164,39 +132,30 @@ ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid) } Error -ProcessPOSIX::DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) -{ - return DoAttachToProcessWithID(pid); -} - -Error ProcessPOSIX::WillLaunch(Module* module) { Error error; return error; } -const char * -ProcessPOSIX::GetFilePath(const lldb_private::FileAction *file_action, const char *default_path, - const char *dbg_pts_path) +FileSpec +ProcessPOSIX::GetFileSpec(const lldb_private::FileAction *file_action, + const FileSpec &default_file_spec, + const FileSpec &dbg_pts_file_spec) { - const char *path = NULL; + FileSpec file_spec{}; - if (file_action) + if (file_action && file_action->GetAction() == FileAction::eFileActionOpen) { - if (file_action->GetAction() == FileAction::eFileActionOpen) - { - path = file_action->GetPath(); - // By default the stdio paths passed in will be pseudo-terminal - // (/dev/pts). If so, convert to using a different default path - // instead to redirect I/O to the debugger console. This should - // also handle user overrides to /dev/null or a different file. - if (!path || (dbg_pts_path && - ::strncmp(path, dbg_pts_path, ::strlen(dbg_pts_path)) == 0)) - path = default_path; - } + file_spec = file_action->GetFileSpec(); + // By default the stdio paths passed in will be pseudo-terminal + // (/dev/pts). If so, convert to using a different default path + // instead to redirect I/O to the debugger console. This should + // also handle user overrides to /dev/null or a different file. + if (!file_spec || file_spec == dbg_pts_file_spec) + file_spec = default_file_spec; } - return path; + return file_spec; } Error @@ -206,46 +165,46 @@ ProcessPOSIX::DoLaunch (Module *module, Error error; assert(m_monitor == NULL); - const char* working_dir = launch_info.GetWorkingDirectory(); - if (working_dir) { - FileSpec WorkingDir(working_dir, true); - if (!WorkingDir || WorkingDir.GetFileType() != FileSpec::eFileTypeDirectory) - { - error.SetErrorStringWithFormat("No such file or directory: %s", working_dir); - return error; - } + FileSpec working_dir = launch_info.GetWorkingDirectory(); + if (working_dir && + (!working_dir.ResolvePath() || + working_dir.GetFileType() != FileSpec::eFileTypeDirectory)) + { + error.SetErrorStringWithFormat("No such file or directory: %s", + working_dir.GetCString()); + return error; } SetPrivateState(eStateLaunching); const lldb_private::FileAction *file_action; - // Default of NULL will mean to use existing open file descriptors - const char *stdin_path = NULL; - const char *stdout_path = NULL; - const char *stderr_path = NULL; + // Default of empty will mean to use existing open file descriptors + FileSpec stdin_file_spec{}; + FileSpec stdout_file_spec{}; + FileSpec stderr_file_spec{}; - const char * dbg_pts_path = launch_info.GetPTY().GetSlaveName(NULL,0); + const FileSpec dbg_pts_file_spec{launch_info.GetPTY().GetSlaveName(NULL,0), false}; file_action = launch_info.GetFileActionForFD (STDIN_FILENO); - stdin_path = GetFilePath(file_action, stdin_path, dbg_pts_path); + stdin_file_spec = GetFileSpec(file_action, stdin_file_spec, dbg_pts_file_spec); file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); - stdout_path = GetFilePath(file_action, stdout_path, dbg_pts_path); + stdout_file_spec = GetFileSpec(file_action, stdout_file_spec, dbg_pts_file_spec); file_action = launch_info.GetFileActionForFD (STDERR_FILENO); - stderr_path = GetFilePath(file_action, stderr_path, dbg_pts_path); - - m_monitor = new ProcessMonitor (this, - module, - launch_info.GetArguments().GetConstArgumentVector(), - launch_info.GetEnvironmentEntries().GetConstArgumentVector(), - stdin_path, - stdout_path, - stderr_path, - working_dir, - launch_info, - error); + stderr_file_spec = GetFileSpec(file_action, stderr_file_spec, dbg_pts_file_spec); + + m_monitor = new ProcessMonitor(this, + module, + launch_info.GetArguments().GetConstArgumentVector(), + launch_info.GetEnvironmentEntries().GetConstArgumentVector(), + stdin_file_spec, + stdout_file_spec, + stderr_file_spec, + working_dir, + launch_info, + error); m_module = module; @@ -426,13 +385,11 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) } else { - StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); } } else { - StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); } break; @@ -468,7 +425,6 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) case ProcessMessage::eCrashMessage: assert(thread); thread->SetState(eStateStopped); - StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); break; @@ -481,7 +437,6 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) } assert(thread); thread->SetState(eStateStopped); - StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); break; } @@ -490,7 +445,6 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) { assert(thread); thread->SetState(eStateStopped); - StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); break; } @@ -500,12 +454,6 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) m_message_queue.push(message); } -void -ProcessPOSIX::StopAllThreads(lldb::tid_t stop_tid) -{ - // FIXME: Will this work the same way on FreeBSD and Linux? -} - bool ProcessPOSIX::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid) { @@ -671,6 +619,33 @@ ProcessPOSIX::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site) assert(false && "CPU type not supported!"); break; + case llvm::Triple::arm: + { + // The ARM reference recommends the use of 0xe7fddefe and 0xdefe + // but the linux kernel does otherwise. + static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 }; + static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; + + lldb::BreakpointLocationSP bp_loc_sp (bp_site->GetOwnerAtIndex (0)); + AddressClass addr_class = eAddressClassUnknown; + + if (bp_loc_sp) + addr_class = bp_loc_sp->GetAddress ().GetAddressClass (); + + if (addr_class == eAddressClassCodeAlternateISA + || (addr_class == eAddressClassUnknown + && bp_loc_sp->GetAddress().GetOffset() & 1)) + { + opcode = g_thumb_breakpoint_opcode; + opcode_size = sizeof(g_thumb_breakpoint_opcode); + } + else + { + opcode = g_arm_breakpoint_opcode; + opcode_size = sizeof(g_arm_breakpoint_opcode); + } + } + break; case llvm::Triple::aarch64: opcode = g_aarch64_opcode; opcode_size = sizeof(g_aarch64_opcode); diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.h b/source/Plugins/Process/FreeBSD/ProcessPOSIX.h index f152356b3093..70694cb9b193 100644 --- a/source/Plugins/Process/POSIX/ProcessPOSIX.h +++ b/source/Plugins/Process/FreeBSD/ProcessPOSIX.h @@ -41,104 +41,101 @@ public: //------------------------------------------------------------------ // Process protocol. //------------------------------------------------------------------ - virtual void + void Finalize() override; - virtual bool + bool CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override; - virtual lldb_private::Error + lldb_private::Error WillLaunch(lldb_private::Module *module) override; - virtual lldb_private::Error - DoAttachToProcessWithID(lldb::pid_t pid) override; - - virtual lldb_private::Error + lldb_private::Error DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; - virtual lldb_private::Error + lldb_private::Error DoLaunch (lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; - virtual void + void DidLaunch() override; - virtual lldb_private::Error + lldb_private::Error DoResume() override; - virtual lldb_private::Error + lldb_private::Error DoHalt(bool &caused_stop) override; - virtual lldb_private::Error + lldb_private::Error DoDetach(bool keep_stopped) override = 0; - virtual lldb_private::Error + lldb_private::Error DoSignal(int signal) override; - virtual lldb_private::Error + lldb_private::Error DoDestroy() override; - virtual void + void DoDidExec() override; - virtual void + void RefreshStateAfterStop() override; - virtual bool + bool IsAlive() override; - virtual size_t + size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, lldb_private::Error &error) override; - virtual size_t + size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Error &error) override; - virtual lldb::addr_t + lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, lldb_private::Error &error) override; - virtual lldb_private::Error + lldb_private::Error DoDeallocateMemory(lldb::addr_t ptr) override; virtual size_t GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); - virtual lldb_private::Error + lldb_private::Error EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; - virtual lldb_private::Error + lldb_private::Error DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; - virtual lldb_private::Error + lldb_private::Error EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; - virtual lldb_private::Error + lldb_private::Error DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; - virtual lldb_private::Error + lldb_private::Error GetWatchpointSupportInfo(uint32_t &num) override; - virtual lldb_private::Error + lldb_private::Error GetWatchpointSupportInfo(uint32_t &num, bool &after) override; virtual uint32_t UpdateThreadListIfNeeded(); - virtual bool + bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override = 0; virtual lldb::ByteOrder GetByteOrder() const; - virtual lldb::addr_t + lldb::addr_t GetImageInfoAddress() override; - virtual size_t + size_t PutSTDIN(const char *buf, size_t len, lldb_private::Error &error) override; const lldb::DataBufferSP @@ -154,13 +151,10 @@ public: ProcessMonitor & GetMonitor() { assert(m_monitor); return *m_monitor; } - const char *GetFilePath(const lldb_private::FileAction *file_action, const char *default_path, - const char *dbg_pts_path); - - /// Stops all threads in the process. - /// The \p stop_tid parameter indicates the thread which initiated the stop. - virtual void - StopAllThreads(lldb::tid_t stop_tid); + lldb_private::FileSpec + GetFileSpec(const lldb_private::FileAction *file_action, + const lldb_private::FileSpec &default_file_spec, + const lldb_private::FileSpec &dbg_pts_file_spec); /// Adds the thread to the list of threads for which we have received the initial stopping signal. /// The \p stop_tid parameter indicates the thread which the stop happened for. diff --git a/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp new file mode 100644 index 000000000000..ac2f81d53929 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.cpp @@ -0,0 +1,322 @@ +//===-- RegisterContextPOSIXProcessMonitor_arm.cpp -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" + +#include "RegisterContextPOSIX_arm.h" +#include "ProcessPOSIX.h" +#include "RegisterContextPOSIXProcessMonitor_arm.h" +#include "ProcessMonitor.h" + +using namespace lldb_private; +using namespace lldb; + +#define REG_CONTEXT_SIZE (GetGPRSize()) + +RegisterContextPOSIXProcessMonitor_arm::RegisterContextPOSIXProcessMonitor_arm(Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info) + : RegisterContextPOSIX_arm(thread, concrete_frame_idx, register_info) +{ +} + +ProcessMonitor & +RegisterContextPOSIXProcessMonitor_arm::GetMonitor() +{ + ProcessSP base = CalculateProcess(); + ProcessPOSIX *process = static_cast<ProcessPOSIX*>(base.get()); + return process->GetMonitor(); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ReadGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadGPR(m_thread.GetID(), &m_gpr_arm, GetGPRSize()); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ReadFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadFPR(m_thread.GetID(), &m_fpr, sizeof(m_fpr)); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::WriteGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteGPR(m_thread.GetID(), &m_gpr_arm, GetGPRSize()); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::WriteFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteFPR(m_thread.GetID(), &m_fpr, sizeof(m_fpr)); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ReadRegister(const unsigned reg, + RegisterValue &value) +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadRegisterValue(m_thread.GetID(), + GetRegisterOffset(reg), + GetRegisterName(reg), + GetRegisterSize(reg), + value); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::WriteRegister(const unsigned reg, + const RegisterValue &value) +{ + unsigned reg_to_write = reg; + RegisterValue value_to_write = value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) + { + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); + + // Read the full register. + if (ReadRegister(full_reg_info, full_value)) + { + Error error; + ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData (full_reg_info, + dst, + sizeof(dst), + byte_order, + error); + if (error.Success() && dest_size) + { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the source data. + const uint32_t src_size = value.GetAsMemoryData (reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) + { + // Copy the src bytes to the destination. + memcpy (dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(full_reg_info); + reg_to_write = full_reg; + } + } + } + } + + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteRegisterValue(m_thread.GetID(), + GetRegisterOffset(reg_to_write), + GetRegisterName(reg_to_write), + value_to_write); +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + if (!reg_info) + return false; + + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (IsFPR(reg)) + { + if (!ReadFPR()) + return false; + } + else + { + return ReadRegister(reg, value); + } + + // Get pointer to m_fpr variable and set the data from it. + assert (reg_info->byte_offset < sizeof m_fpr); + uint8_t *src = (uint8_t *)&m_fpr + reg_info->byte_offset; + switch (reg_info->byte_size) + { + case 2: + value.SetUInt16(*(uint16_t *)src); + return true; + case 4: + value.SetUInt32(*(uint32_t *)src); + return true; + case 8: + value.SetUInt64(*(uint64_t *)src); + return true; + default: + assert(false && "Unhandled data size."); + return false; + } +} + +bool +RegisterContextPOSIXProcessMonitor_arm::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (IsGPR(reg)) + { + return WriteRegister(reg, value); + } + else if (IsFPR(reg)) + { + return WriteFPR(); + } + + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + bool success = false; + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && ReadGPR () && ReadFPR ()) + { + uint8_t *dst = data_sp->GetBytes(); + success = dst != 0; + + if (success) + { + ::memcpy (dst, &m_gpr_arm, GetGPRSize()); + dst += GetGPRSize(); + ::memcpy (dst, &m_fpr, sizeof(m_fpr)); + } + } + return success; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::WriteAllRegisterValues(const DataBufferSP &data_sp) +{ + bool success = false; + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + uint8_t *src = data_sp->GetBytes(); + if (src) + { + ::memcpy (&m_gpr_arm, src, GetGPRSize()); + + if (WriteGPR()) + { + src += GetGPRSize(); + ::memcpy (&m_fpr, src, sizeof(m_fpr)); + + success = WriteFPR(); + } + } + } + return success; +} + +uint32_t +RegisterContextPOSIXProcessMonitor_arm::SetHardwareWatchpoint(addr_t addr, size_t size, + bool read, bool write) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + uint32_t hw_index; + + for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) + { + if (IsWatchpointVacant(hw_index)) + return SetHardwareWatchpointWithIndex(addr, size, + read, write, + hw_index); + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ClearHardwareWatchpoint(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::HardwareSingleStep(bool enable) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::UpdateAfterBreakpoint() +{ + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + return true; +} + +unsigned +RegisterContextPOSIXProcessMonitor_arm::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < k_num_registers_arm; reg++) + { + if (GetRegisterInfo()[reg].byte_offset == offset) + break; + } + assert(reg < k_num_registers_arm && "Invalid register offset."); + return reg; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::IsWatchpointHit(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::ClearWatchpointHits() +{ + return false; +} + +addr_t +RegisterContextPOSIXProcessMonitor_arm::GetWatchpointAddress(uint32_t hw_index) +{ + return LLDB_INVALID_ADDRESS; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::IsWatchpointVacant(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_arm::SetHardwareWatchpointWithIndex(addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) +{ + return false; +} + +uint32_t +RegisterContextPOSIXProcessMonitor_arm::NumSupportedHardwareWatchpoints() +{ + return 0; +} + diff --git a/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h new file mode 100644 index 000000000000..12a43c77837c --- /dev/null +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm.h @@ -0,0 +1,95 @@ +//===-- RegisterContextPOSIXProcessMonitor_arm.h --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextPOSIXProcessMonitor_arm_H_ +#define liblldb_RegisterContextPOSIXProcessMonitor_arm_H_ + +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h" + +class RegisterContextPOSIXProcessMonitor_arm: + public RegisterContextPOSIX_arm, + public POSIXBreakpointProtocol +{ +public: + RegisterContextPOSIXProcessMonitor_arm(lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + +protected: + bool + ReadGPR(); + + bool + ReadFPR(); + + bool + WriteGPR(); + + bool + WriteFPR(); + + // lldb_private::RegisterContext + bool + ReadRegister(const unsigned reg, lldb_private::RegisterValue &value); + + bool + WriteRegister(const unsigned reg, const lldb_private::RegisterValue &value); + + bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write); + + bool + ClearHardwareWatchpoint(uint32_t hw_index); + + bool + HardwareSingleStep(bool enable); + + // POSIXBreakpointProtocol + bool + UpdateAfterBreakpoint(); + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + bool + IsWatchpointHit(uint32_t hw_index); + + bool + ClearWatchpointHits(); + + lldb::addr_t + GetWatchpointAddress(uint32_t hw_index); + + bool + IsWatchpointVacant(uint32_t hw_index); + + bool + SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read, bool write, uint32_t hw_index); + + uint32_t + NumSupportedHardwareWatchpoints(); + +private: + ProcessMonitor & + GetMonitor(); +}; + +#endif diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp index ec34d9e28161..d79def52499f 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.cpp +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.cpp @@ -7,16 +7,20 @@ // //===---------------------------------------------------------------------===// -#include "lldb/Target/Thread.h" +#include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" #include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h" #include "ProcessPOSIX.h" +#include "ProcessMonitor.h" #include "RegisterContextPOSIXProcessMonitor_arm64.h" -#include "Plugins/Process/Linux/ProcessMonitor.h" #define REG_CONTEXT_SIZE (GetGPRSize()) +using namespace lldb; +using namespace lldb_private; + RegisterContextPOSIXProcessMonitor_arm64::RegisterContextPOSIXProcessMonitor_arm64(lldb_private::Thread &thread, uint32_t concrete_frame_idx, lldb_private::RegisterInfoInterface *register_info) diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.h b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h index eb24d4852ab8..eb24d4852ab8 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.h +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_arm64.h diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp index 6717d20da056..893a0f22b6f8 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.cpp +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.cpp @@ -7,13 +7,14 @@ // //===---------------------------------------------------------------------===// -#include "lldb/Target/Thread.h" +#include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" #include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h" #include "ProcessPOSIX.h" +#include "ProcessMonitor.h" #include "RegisterContextPOSIXProcessMonitor_mips64.h" -#include "Plugins/Process/Linux/ProcessMonitor.h" using namespace lldb_private; using namespace lldb; diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.h b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h index 79e4468b1adf..79e4468b1adf 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.h +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_mips64.h diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp index 80e1c1984225..8c12b62a202f 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.cpp +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.cpp @@ -7,8 +7,9 @@ // //===---------------------------------------------------------------------===// -#include "lldb/Target/Thread.h" +#include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" #include "RegisterContextPOSIX_powerpc.h" #include "ProcessPOSIX.h" diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.h b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h index 5c686df4836f..5c686df4836f 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.h +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_powerpc.h diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.cpp b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp index 1956e4584fa9..9245441f659f 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.cpp +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.cpp @@ -7,16 +7,13 @@ // //===---------------------------------------------------------------------===// -#include "lldb/Target/Thread.h" +#include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" -#include "Plugins/Process/POSIX/ProcessPOSIX.h" +#include "Plugins/Process/FreeBSD/ProcessPOSIX.h" #include "RegisterContextPOSIXProcessMonitor_x86.h" -#if defined(__FreeBSD__) #include "Plugins/Process/FreeBSD/ProcessMonitor.h" -#else -#include "Plugins/Process/Linux/ProcessMonitor.h" -#endif using namespace lldb_private; using namespace lldb; diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.h b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h index 2afb195c4c36..2afb195c4c36 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.h +++ b/source/Plugins/Process/FreeBSD/RegisterContextPOSIXProcessMonitor_x86.h diff --git a/source/Plugins/Process/POSIX/CrashReason.cpp b/source/Plugins/Process/POSIX/CrashReason.cpp index 4dd91a6f1de8..6de13f470c5e 100644 --- a/source/Plugins/Process/POSIX/CrashReason.cpp +++ b/source/Plugins/Process/POSIX/CrashReason.cpp @@ -134,69 +134,69 @@ GetCrashReasonString (CrashReason reason, lldb::addr_t fault_addr) break; case CrashReason::eInvalidAddress: - str = "invalid address"; + str = "signal SIGSEGV: invalid address"; AppendFaultAddr (str, fault_addr); break; case CrashReason::ePrivilegedAddress: - str = "address access protected"; + str = "signal SIGSEGV: address access protected"; AppendFaultAddr (str, fault_addr); break; case CrashReason::eIllegalOpcode: - str = "illegal instruction"; + str = "signal SIGILL: illegal instruction"; break; case CrashReason::eIllegalOperand: - str = "illegal instruction operand"; + str = "signal SIGILL: illegal instruction operand"; break; case CrashReason::eIllegalAddressingMode: - str = "illegal addressing mode"; + str = "signal SIGILL: illegal addressing mode"; break; case CrashReason::eIllegalTrap: - str = "illegal trap"; + str = "signal SIGILL: illegal trap"; break; case CrashReason::ePrivilegedOpcode: - str = "privileged instruction"; + str = "signal SIGILL: privileged instruction"; break; case CrashReason::ePrivilegedRegister: - str = "privileged register"; + str = "signal SIGILL: privileged register"; break; case CrashReason::eCoprocessorError: - str = "coprocessor error"; + str = "signal SIGILL: coprocessor error"; break; case CrashReason::eInternalStackError: - str = "internal stack error"; + str = "signal SIGILL: internal stack error"; break; case CrashReason::eIllegalAlignment: - str = "illegal alignment"; + str = "signal SIGBUS: illegal alignment"; break; case CrashReason::eIllegalAddress: - str = "illegal address"; + str = "signal SIGBUS: illegal address"; break; case CrashReason::eHardwareError: - str = "hardware error"; + str = "signal SIGBUS: hardware error"; break; case CrashReason::eIntegerDivideByZero: - str = "integer divide by zero"; + str = "signal SIGFPE: integer divide by zero"; break; case CrashReason::eIntegerOverflow: - str = "integer overflow"; + str = "signal SIGFPE: integer overflow"; break; case CrashReason::eFloatDivideByZero: - str = "floating point divide by zero"; + str = "signal SIGFPE: floating point divide by zero"; break; case CrashReason::eFloatOverflow: - str = "floating point overflow"; + str = "signal SIGFPE: floating point overflow"; break; case CrashReason::eFloatUnderflow: - str = "floating point underflow"; + str = "signal SIGFPE: floating point underflow"; break; case CrashReason::eFloatInexactResult: - str = "inexact floating point result"; + str = "signal SIGFPE: inexact floating point result"; break; case CrashReason::eFloatInvalidOperation: - str = "invalid floating point operation"; + str = "signal SIGFPE: invalid floating point operation"; break; case CrashReason::eFloatSubscriptRange: - str = "invalid floating point subscript range"; + str = "signal SIGFPE: invalid floating point subscript range"; break; } diff --git a/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp index 624ca87b883a..b259804a3a27 100644 --- a/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp +++ b/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp @@ -9,10 +9,11 @@ #include "ProcessPOSIXLog.h" +#include <mutex> + #include "lldb/Interpreter/Args.h" #include "lldb/Core/StreamFile.h" -#include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" using namespace lldb; @@ -33,6 +34,22 @@ GetLog () return g_log; } +void +ProcessPOSIXLog::Initialize(ConstString name) +{ + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, [name](){ + Log::Callbacks log_callbacks = { + DisableLog, + EnableLog, + ListLogCategories + }; + + Log::RegisterLogChannel (name, log_callbacks); + RegisterPluginName(name); + }); +} Log * ProcessPOSIXLog::GetLogIfAllCategoriesSet (uint32_t mask) diff --git a/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/source/Plugins/Process/POSIX/ProcessPOSIXLog.h index a1e2e3747d21..7edd839152e6 100644 --- a/source/Plugins/Process/POSIX/ProcessPOSIXLog.h +++ b/source/Plugins/Process/POSIX/ProcessPOSIXLog.h @@ -43,6 +43,12 @@ class ProcessPOSIXLog static const char *m_pluginname; public: + // --------------------------------------------------------------------- + // Public Static Methods + // --------------------------------------------------------------------- + static void + Initialize(lldb_private::ConstString name); + static void RegisterPluginName(const char *pluginName) { diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp index 25a195e11a03..4eff442c1a0d 100644 --- a/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp +++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp @@ -7,22 +7,18 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "DynamicRegisterInfo.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes -#include "lldb/Host/StringConvert.h" +#include "lldb/Core/ArchSpec.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredData.h" #include "lldb/DataFormatters/FormatManager.h" - -#ifndef LLDB_DISABLE_PYTHON -#include "lldb/Interpreter/PythonDataObjects.h" -#endif +#include "lldb/Host/StringConvert.h" using namespace lldb; using namespace lldb_private; @@ -39,7 +35,8 @@ DynamicRegisterInfo::DynamicRegisterInfo () : { } -DynamicRegisterInfo::DynamicRegisterInfo (const lldb_private::PythonDictionary &dict, ByteOrder byte_order) : +DynamicRegisterInfo::DynamicRegisterInfo(const lldb_private::StructuredData::Dictionary &dict, + const lldb_private::ArchSpec &arch) : m_regs (), m_sets (), m_set_reg_nums (), @@ -49,30 +46,27 @@ DynamicRegisterInfo::DynamicRegisterInfo (const lldb_private::PythonDictionary & m_reg_data_byte_size (0), m_finalized (false) { - SetRegisterInfo (dict, byte_order); + SetRegisterInfo (dict, arch); } DynamicRegisterInfo::~DynamicRegisterInfo () { } - size_t -DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict, - ByteOrder byte_order) +DynamicRegisterInfo::SetRegisterInfo(const StructuredData::Dictionary &dict, const ArchSpec &arch) { assert(!m_finalized); -#ifndef LLDB_DISABLE_PYTHON - PythonList sets (dict.GetItemForKey("sets")); - if (sets) + StructuredData::Array *sets = nullptr; + if (dict.GetValueForKeyAsArray("sets", sets)) { - const uint32_t num_sets = sets.GetSize(); + const uint32_t num_sets = sets->GetSize(); for (uint32_t i=0; i<num_sets; ++i) { - PythonString py_set_name(sets.GetItemAtIndex(i)); + std::string set_name_str; ConstString set_name; - if (py_set_name) - set_name.SetCString(py_set_name.GetString()); + if (sets->GetItemAtIndexAsString(i, set_name_str)) + set_name.SetCString(set_name_str.c_str()); if (set_name) { RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; @@ -87,346 +81,312 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict } m_set_reg_nums.resize(m_sets.size()); } - PythonList regs (dict.GetItemForKey("registers")); - if (regs) - { - const uint32_t num_regs = regs.GetSize(); - PythonString name_pystr("name"); - PythonString altname_pystr("alt-name"); - PythonString bitsize_pystr("bitsize"); - PythonString offset_pystr("offset"); - PythonString encoding_pystr("encoding"); - PythonString format_pystr("format"); - PythonString set_pystr("set"); - PythonString gcc_pystr("gcc"); - PythonString dwarf_pystr("dwarf"); - PythonString generic_pystr("generic"); - PythonString slice_pystr("slice"); - PythonString composite_pystr("composite"); - PythonString invalidate_regs_pystr("invalidate-regs"); - + StructuredData::Array *regs = nullptr; + if (!dict.GetValueForKeyAsArray("registers", regs)) + return 0; + + const uint32_t num_regs = regs->GetSize(); // typedef std::map<std::string, std::vector<std::string> > InvalidateNameMap; // InvalidateNameMap invalidate_map; - for (uint32_t i=0; i<num_regs; ++i) + for (uint32_t i = 0; i < num_regs; ++i) + { + StructuredData::Dictionary *reg_info_dict = nullptr; + if (!regs->GetItemAtIndexAsDictionary(i, reg_info_dict)) { - PythonDictionary reg_info_dict(regs.GetItemAtIndex(i)); - if (reg_info_dict) - { - // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, - RegisterInfo reg_info; - std::vector<uint32_t> value_regs; - std::vector<uint32_t> invalidate_regs; - memset(®_info, 0, sizeof(reg_info)); - - reg_info.name = ConstString (reg_info_dict.GetItemForKeyAsString(name_pystr)).GetCString(); - if (reg_info.name == NULL) - { - Clear(); - printf("error: registers must have valid names\n"); - reg_info_dict.Dump(); - return 0; - } - - reg_info.alt_name = ConstString (reg_info_dict.GetItemForKeyAsString(altname_pystr)).GetCString(); - - reg_info.byte_offset = reg_info_dict.GetItemForKeyAsInteger(offset_pystr, UINT32_MAX); + Clear(); + printf("error: items in the 'registers' array must be dictionaries\n"); + regs->DumpToStdout(); + return 0; + } - if (reg_info.byte_offset == UINT32_MAX) + // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, + // 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + RegisterInfo reg_info; + std::vector<uint32_t> value_regs; + std::vector<uint32_t> invalidate_regs; + memset(®_info, 0, sizeof(reg_info)); + + ConstString name_val; + ConstString alt_name_val; + if (!reg_info_dict->GetValueForKeyAsString("name", name_val, nullptr)) + { + Clear(); + printf("error: registers must have valid names and offsets\n"); + reg_info_dict->DumpToStdout(); + return 0; + } + reg_info.name = name_val.GetCString(); + reg_info_dict->GetValueForKeyAsString("alt-name", alt_name_val, nullptr); + reg_info.alt_name = alt_name_val.GetCString(); + + reg_info_dict->GetValueForKeyAsInteger("offset", reg_info.byte_offset, UINT32_MAX); + + const ByteOrder byte_order = arch.GetByteOrder(); + + if (reg_info.byte_offset == UINT32_MAX) + { + // No offset for this register, see if the register has a value expression + // which indicates this register is part of another register. Value expressions + // are things like "rax[31:0]" which state that the current register's value + // is in a concrete register "rax" in bits 31:0. If there is a value expression + // we can calculate the offset + bool success = false; + std::string slice_str; + if (reg_info_dict->GetValueForKeyAsString("slice", slice_str, nullptr)) + { + // Slices use the following format: + // REGNAME[MSBIT:LSBIT] + // REGNAME - name of the register to grab a slice of + // MSBIT - the most significant bit at which the current register value starts at + // LSBIT - the least significant bit at which the current register value ends at + static RegularExpression g_bitfield_regex("([A-Za-z_][A-Za-z0-9_]*)\\[([0-9]+):([0-9]+)\\]"); + RegularExpression::Match regex_match(3); + if (g_bitfield_regex.Execute(slice_str.c_str(), ®ex_match)) { - // No offset for this register, see if the register has a value expression - // which indicates this register is part of another register. Value expressions - // are things like "rax[31:0]" which state that the current register's value - // is in a concrete register "rax" in bits 31:0. If there is a value expression - // we can calculate the offset - bool success = false; - const char *slice_cstr = reg_info_dict.GetItemForKeyAsString(slice_pystr); - if (slice_cstr) + llvm::StringRef reg_name_str; + std::string msbit_str; + std::string lsbit_str; + if (regex_match.GetMatchAtIndex(slice_str.c_str(), 1, reg_name_str) && + regex_match.GetMatchAtIndex(slice_str.c_str(), 2, msbit_str) && + regex_match.GetMatchAtIndex(slice_str.c_str(), 3, lsbit_str)) { - // Slices use the following format: - // REGNAME[MSBIT:LSBIT] - // REGNAME - name of the register to grab a slice of - // MSBIT - the most significant bit at which the current register value starts at - // LSBIT - the least significant bit at which the current register value ends at - static RegularExpression g_bitfield_regex("([A-Za-z_][A-Za-z0-9_]*)\\[([0-9]+):([0-9]+)\\]"); - RegularExpression::Match regex_match(3); - if (g_bitfield_regex.Execute(slice_cstr, ®ex_match)) + const uint32_t msbit = StringConvert::ToUInt32(msbit_str.c_str(), UINT32_MAX); + const uint32_t lsbit = StringConvert::ToUInt32(lsbit_str.c_str(), UINT32_MAX); + if (msbit != UINT32_MAX && lsbit != UINT32_MAX) { - llvm::StringRef reg_name_str; - std::string msbit_str; - std::string lsbit_str; - if (regex_match.GetMatchAtIndex(slice_cstr, 1, reg_name_str) && - regex_match.GetMatchAtIndex(slice_cstr, 2, msbit_str) && - regex_match.GetMatchAtIndex(slice_cstr, 3, lsbit_str)) + if (msbit > lsbit) { - const uint32_t msbit = StringConvert::ToUInt32(msbit_str.c_str(), UINT32_MAX); - const uint32_t lsbit = StringConvert::ToUInt32(lsbit_str.c_str(), UINT32_MAX); - if (msbit != UINT32_MAX && lsbit != UINT32_MAX) + const uint32_t msbyte = msbit / 8; + const uint32_t lsbyte = lsbit / 8; + + ConstString containing_reg_name(reg_name_str); + + RegisterInfo *containing_reg_info = GetRegisterInfo(containing_reg_name); + if (containing_reg_info) { - if (msbit > lsbit) + const uint32_t max_bit = containing_reg_info->byte_size * 8; + if (msbit < max_bit && lsbit < max_bit) { - const uint32_t msbyte = msbit / 8; - const uint32_t lsbyte = lsbit / 8; + m_invalidate_regs_map[containing_reg_info->kinds[eRegisterKindLLDB]].push_back(i); + m_value_regs_map[i].push_back(containing_reg_info->kinds[eRegisterKindLLDB]); + m_invalidate_regs_map[i].push_back(containing_reg_info->kinds[eRegisterKindLLDB]); - ConstString containing_reg_name(reg_name_str); - - RegisterInfo *containing_reg_info = GetRegisterInfo (containing_reg_name); - if (containing_reg_info) + if (byte_order == eByteOrderLittle) + { + success = true; + reg_info.byte_offset = containing_reg_info->byte_offset + lsbyte; + } + else if (byte_order == eByteOrderBig) { - const uint32_t max_bit = containing_reg_info->byte_size * 8; - if (msbit < max_bit && lsbit < max_bit) - { - m_invalidate_regs_map[containing_reg_info->kinds[eRegisterKindLLDB]].push_back(i); - m_value_regs_map[i].push_back(containing_reg_info->kinds[eRegisterKindLLDB]); - m_invalidate_regs_map[i].push_back(containing_reg_info->kinds[eRegisterKindLLDB]); - - if (byte_order == eByteOrderLittle) - { - success = true; - reg_info.byte_offset = containing_reg_info->byte_offset + lsbyte; - } - else if (byte_order == eByteOrderBig) - { - success = true; - reg_info.byte_offset = containing_reg_info->byte_offset + msbyte; - } - else - { - assert(!"Invalid byte order"); - } - } - else - { - if (msbit > max_bit) - printf("error: msbit (%u) must be less than the bitsize of the register (%u)\n", msbit, max_bit); - else - printf("error: lsbit (%u) must be less than the bitsize of the register (%u)\n", lsbit, max_bit); - } + success = true; + reg_info.byte_offset = containing_reg_info->byte_offset + msbyte; } else { - printf("error: invalid concrete register \"%s\"\n", containing_reg_name.GetCString()); + assert(!"Invalid byte order"); } } else { - printf("error: msbit (%u) must be greater than lsbit (%u)\n", msbit, lsbit); + if (msbit > max_bit) + printf("error: msbit (%u) must be less than the bitsize of the register (%u)\n", msbit, + max_bit); + else + printf("error: lsbit (%u) must be less than the bitsize of the register (%u)\n", lsbit, + max_bit); } } else { - printf("error: msbit (%u) and lsbit (%u) must be valid\n", msbit, lsbit); + printf("error: invalid concrete register \"%s\"\n", containing_reg_name.GetCString()); } } else { - // TODO: print error invalid slice string that doesn't follow the format - printf("error: failed to extract regex matches for parsing the register bitfield regex\n"); - + printf("error: msbit (%u) must be greater than lsbit (%u)\n", msbit, lsbit); } } else { - // TODO: print error invalid slice string that doesn't follow the format - printf("error: failed to match against register bitfield regex\n"); + printf("error: msbit (%u) and lsbit (%u) must be valid\n", msbit, lsbit); } } else { - PythonList composite_reg_list (reg_info_dict.GetItemForKey(composite_pystr)); - if (composite_reg_list) + // TODO: print error invalid slice string that doesn't follow the format + printf("error: failed to extract regex matches for parsing the register bitfield regex\n"); + } + } + else + { + // TODO: print error invalid slice string that doesn't follow the format + printf("error: failed to match against register bitfield regex\n"); + } + } + else + { + StructuredData::Array *composite_reg_list = nullptr; + if (reg_info_dict->GetValueForKeyAsArray("composite", composite_reg_list)) + { + const size_t num_composite_regs = composite_reg_list->GetSize(); + if (num_composite_regs > 0) + { + uint32_t composite_offset = UINT32_MAX; + for (uint32_t composite_idx = 0; composite_idx < num_composite_regs; ++composite_idx) { - const size_t num_composite_regs = composite_reg_list.GetSize(); - if (num_composite_regs > 0) + ConstString composite_reg_name; + if (composite_reg_list->GetItemAtIndexAsString(composite_idx, composite_reg_name, nullptr)) { - uint32_t composite_offset = UINT32_MAX; - for (uint32_t composite_idx=0; composite_idx<num_composite_regs; ++composite_idx) + RegisterInfo *composite_reg_info = GetRegisterInfo(composite_reg_name); + if (composite_reg_info) { - PythonString composite_reg_name_pystr(composite_reg_list.GetItemAtIndex(composite_idx)); - if (composite_reg_name_pystr) - { - ConstString composite_reg_name(composite_reg_name_pystr.GetString()); - if (composite_reg_name) - { - RegisterInfo *composite_reg_info = GetRegisterInfo (composite_reg_name); - if (composite_reg_info) - { - if (composite_offset > composite_reg_info->byte_offset) - composite_offset = composite_reg_info->byte_offset; - m_value_regs_map[i].push_back(composite_reg_info->kinds[eRegisterKindLLDB]); - m_invalidate_regs_map[composite_reg_info->kinds[eRegisterKindLLDB]].push_back(i); - m_invalidate_regs_map[i].push_back(composite_reg_info->kinds[eRegisterKindLLDB]); - } - else - { - // TODO: print error invalid slice string that doesn't follow the format - printf("error: failed to find composite register by name: \"%s\"\n", composite_reg_name.GetCString()); - } - } - else - { - printf("error: 'composite' key contained an empty string\n"); - } - } - else - { - printf("error: 'composite' list value wasn't a python string\n"); - } - } - if (composite_offset != UINT32_MAX) - { - reg_info.byte_offset = composite_offset; - success = m_value_regs_map.find(i) != m_value_regs_map.end(); + composite_offset = std::min(composite_offset, composite_reg_info->byte_offset); + m_value_regs_map[i].push_back(composite_reg_info->kinds[eRegisterKindLLDB]); + m_invalidate_regs_map[composite_reg_info->kinds[eRegisterKindLLDB]].push_back(i); + m_invalidate_regs_map[i].push_back(composite_reg_info->kinds[eRegisterKindLLDB]); } else { - printf("error: 'composite' registers must specify at least one real register\n"); + // TODO: print error invalid slice string that doesn't follow the format + printf("error: failed to find composite register by name: \"%s\"\n", composite_reg_name.GetCString()); } } else { - printf("error: 'composite' list was empty\n"); + printf("error: 'composite' list value wasn't a python string\n"); } } + if (composite_offset != UINT32_MAX) + { + reg_info.byte_offset = composite_offset; + success = m_value_regs_map.find(i) != m_value_regs_map.end(); + } + else + { + printf("error: 'composite' registers must specify at least one real register\n"); + } } - - - if (!success) + else { - Clear(); - reg_info_dict.Dump(); - return 0; + printf("error: 'composite' list was empty\n"); } } - const int64_t bitsize = reg_info_dict.GetItemForKeyAsInteger(bitsize_pystr, 0); - if (bitsize == 0) - { - Clear(); - printf("error: invalid or missing 'bitsize' key/value pair in register dictionary\n"); - reg_info_dict.Dump(); - return 0; - } + } - reg_info.byte_size = bitsize / 8; - - const char *format_cstr = reg_info_dict.GetItemForKeyAsString(format_pystr); - if (format_cstr) - { - if (Args::StringToFormat(format_cstr, reg_info.format, NULL).Fail()) - { - Clear(); - printf("error: invalid 'format' value in register dictionary\n"); - reg_info_dict.Dump(); - return 0; - } - } - else - { - reg_info.format = (Format)reg_info_dict.GetItemForKeyAsInteger (format_pystr, eFormatHex); - } - - const char *encoding_cstr = reg_info_dict.GetItemForKeyAsString(encoding_pystr); - if (encoding_cstr) - reg_info.encoding = Args::StringToEncoding (encoding_cstr, eEncodingUint); - else - reg_info.encoding = (Encoding)reg_info_dict.GetItemForKeyAsInteger (encoding_pystr, eEncodingUint); + if (!success) + { + Clear(); + reg_info_dict->DumpToStdout(); + return 0; + } + } - const int64_t set = reg_info_dict.GetItemForKeyAsInteger(set_pystr, -1); - if (static_cast<size_t>(set) >= m_sets.size()) - { - Clear(); - printf("error: invalid 'set' value in register dictionary, valid values are 0 - %i\n", (int)set); - reg_info_dict.Dump(); - return 0; - } + int64_t bitsize = 0; + if (!reg_info_dict->GetValueForKeyAsInteger("bitsize", bitsize)) + { + Clear(); + printf("error: invalid or missing 'bitsize' key/value pair in register dictionary\n"); + reg_info_dict->DumpToStdout(); + return 0; + } - // Fill in the register numbers - reg_info.kinds[lldb::eRegisterKindLLDB] = i; - reg_info.kinds[lldb::eRegisterKindGDB] = i; - reg_info.kinds[lldb::eRegisterKindGCC] = reg_info_dict.GetItemForKeyAsInteger(gcc_pystr, LLDB_INVALID_REGNUM); - reg_info.kinds[lldb::eRegisterKindDWARF] = reg_info_dict.GetItemForKeyAsInteger(dwarf_pystr, LLDB_INVALID_REGNUM); - const char *generic_cstr = reg_info_dict.GetItemForKeyAsString(generic_pystr); - if (generic_cstr) - reg_info.kinds[lldb::eRegisterKindGeneric] = Args::StringToGenericRegister (generic_cstr); - else - reg_info.kinds[lldb::eRegisterKindGeneric] = reg_info_dict.GetItemForKeyAsInteger(generic_pystr, LLDB_INVALID_REGNUM); + reg_info.byte_size = bitsize / 8; - // Check if this register invalidates any other register values when it is modified - PythonList invalidate_reg_list (reg_info_dict.GetItemForKey(invalidate_regs_pystr)); - if (invalidate_reg_list) + std::string format_str; + if (reg_info_dict->GetValueForKeyAsString("format", format_str, nullptr)) + { + if (Args::StringToFormat(format_str.c_str(), reg_info.format, NULL).Fail()) + { + Clear(); + printf("error: invalid 'format' value in register dictionary\n"); + reg_info_dict->DumpToStdout(); + return 0; + } + } + else + { + reg_info_dict->GetValueForKeyAsInteger("format", reg_info.format, eFormatHex); + } + + std::string encoding_str; + if (reg_info_dict->GetValueForKeyAsString("encoding", encoding_str)) + reg_info.encoding = Args::StringToEncoding(encoding_str.c_str(), eEncodingUint); + else + reg_info_dict->GetValueForKeyAsInteger("encoding", reg_info.encoding, eEncodingUint); + + size_t set = 0; + if (!reg_info_dict->GetValueForKeyAsInteger<size_t>("set", set, -1) || set >= m_sets.size()) + { + Clear(); + printf("error: invalid 'set' value in register dictionary, valid values are 0 - %i\n", (int)set); + reg_info_dict->DumpToStdout(); + return 0; + } + + // Fill in the register numbers + reg_info.kinds[lldb::eRegisterKindLLDB] = i; + reg_info.kinds[lldb::eRegisterKindGDB] = i; + reg_info_dict->GetValueForKeyAsInteger("gcc", reg_info.kinds[lldb::eRegisterKindGCC], LLDB_INVALID_REGNUM); + reg_info_dict->GetValueForKeyAsInteger("dwarf", reg_info.kinds[lldb::eRegisterKindDWARF], LLDB_INVALID_REGNUM); + std::string generic_str; + if (reg_info_dict->GetValueForKeyAsString("generic", generic_str)) + reg_info.kinds[lldb::eRegisterKindGeneric] = Args::StringToGenericRegister(generic_str.c_str()); + else + reg_info_dict->GetValueForKeyAsInteger("generic", reg_info.kinds[lldb::eRegisterKindGeneric], LLDB_INVALID_REGNUM); + + // Check if this register invalidates any other register values when it is modified + StructuredData::Array *invalidate_reg_list = nullptr; + if (reg_info_dict->GetValueForKeyAsArray("invalidate-regs", invalidate_reg_list)) + { + const size_t num_regs = invalidate_reg_list->GetSize(); + if (num_regs > 0) + { + for (uint32_t idx = 0; idx < num_regs; ++idx) { - const size_t num_regs = invalidate_reg_list.GetSize(); - if (num_regs > 0) + ConstString invalidate_reg_name; + uint64_t invalidate_reg_num; + if (invalidate_reg_list->GetItemAtIndexAsString(idx, invalidate_reg_name)) { - for (uint32_t idx=0; idx<num_regs; ++idx) + RegisterInfo *invalidate_reg_info = GetRegisterInfo(invalidate_reg_name); + if (invalidate_reg_info) { - PythonObject invalidate_reg_object (invalidate_reg_list.GetItemAtIndex(idx)); - PythonString invalidate_reg_name_pystr(invalidate_reg_object); - if (invalidate_reg_name_pystr) - { - ConstString invalidate_reg_name(invalidate_reg_name_pystr.GetString()); - if (invalidate_reg_name) - { - RegisterInfo *invalidate_reg_info = GetRegisterInfo (invalidate_reg_name); - if (invalidate_reg_info) - { - m_invalidate_regs_map[i].push_back(invalidate_reg_info->kinds[eRegisterKindLLDB]); - } - else - { - // TODO: print error invalid slice string that doesn't follow the format - printf("error: failed to find a 'invalidate-regs' register for \"%s\" while parsing register \"%s\"\n", invalidate_reg_name.GetCString(), reg_info.name); - } - } - else - { - printf("error: 'invalidate-regs' list value was an empty string\n"); - } - } - else - { - PythonInteger invalidate_reg_num(invalidate_reg_object); - - if (invalidate_reg_num) - { - const int64_t r = invalidate_reg_num.GetInteger(); - if (r != static_cast<int64_t>(UINT64_MAX)) - m_invalidate_regs_map[i].push_back(r); - else - printf("error: 'invalidate-regs' list value wasn't a valid integer\n"); - } - else - { - printf("error: 'invalidate-regs' list value wasn't a python string or integer\n"); - } - } + m_invalidate_regs_map[i].push_back(invalidate_reg_info->kinds[eRegisterKindLLDB]); + } + else + { + // TODO: print error invalid slice string that doesn't follow the format + printf("error: failed to find a 'invalidate-regs' register for \"%s\" while parsing register \"%s\"\n", + invalidate_reg_name.GetCString(), reg_info.name); } } + else if (invalidate_reg_list->GetItemAtIndexAsInteger(idx, invalidate_reg_num)) + { + if (invalidate_reg_num != UINT64_MAX) + m_invalidate_regs_map[i].push_back(invalidate_reg_num); + else + printf("error: 'invalidate-regs' list value wasn't a valid integer\n"); + } else { - printf("error: 'invalidate-regs' contained an empty list\n"); + printf("error: 'invalidate-regs' list value wasn't a python string or integer\n"); } } - - // Calculate the register offset - const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; - if (m_reg_data_byte_size < end_reg_offset) - m_reg_data_byte_size = end_reg_offset; - - m_regs.push_back (reg_info); - m_set_reg_nums[set].push_back(i); - } else { - Clear(); - printf("error: items in the 'registers' array must be dictionaries\n"); - regs.Dump(); - return 0; + printf("error: 'invalidate-regs' contained an empty list\n"); } } - Finalize (); + + // Calculate the register offset + const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; + + m_regs.push_back(reg_info); + m_set_reg_nums[set].push_back(i); } -#endif + Finalize(arch); return m_regs.size(); } @@ -465,7 +425,7 @@ DynamicRegisterInfo::AddRegister (RegisterInfo ®_info, } void -DynamicRegisterInfo::Finalize () +DynamicRegisterInfo::Finalize (const ArchSpec &arch) { if (m_finalized) return; @@ -560,6 +520,95 @@ DynamicRegisterInfo::Finalize () else m_regs[i].invalidate_regs = NULL; } + + // Check if we need to automatically set the generic registers in case + // they weren't set + bool generic_regs_specified = false; + for (const auto ®: m_regs) + { + if (reg.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) + { + generic_regs_specified = true; + break; + } + } + + if (!generic_regs_specified) + { + switch (arch.GetMachine()) + { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + for (auto ®: m_regs) + { + if (strcmp(reg.name, "pc") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "fp") == 0) || (strcmp(reg.name, "x29") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "lr") == 0) || (strcmp(reg.name, "x30") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + else if ((strcmp(reg.name, "sp") == 0) || (strcmp(reg.name, "x31") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if (strcmp(reg.name, "cpsr") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + for (auto ®: m_regs) + { + if ((strcmp(reg.name, "pc") == 0) || (strcmp(reg.name, "r15") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "sp") == 0) || (strcmp(reg.name, "r13") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if ((strcmp(reg.name, "lr") == 0) || (strcmp(reg.name, "r14") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + else if ((strcmp(reg.name, "r7") == 0) && arch.GetTriple().getVendor() == llvm::Triple::Apple) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "r11") == 0) && arch.GetTriple().getVendor() != llvm::Triple::Apple) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if (strcmp(reg.name, "fp") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if (strcmp(reg.name, "cpsr") == 0) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + case llvm::Triple::x86: + for (auto ®: m_regs) + { + if ((strcmp(reg.name, "eip") == 0) || (strcmp(reg.name, "pc") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "esp") == 0) || (strcmp(reg.name, "sp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if ((strcmp(reg.name, "ebp") == 0) || (strcmp(reg.name, "fp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "eflags") == 0) || (strcmp(reg.name, "flags") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + case llvm::Triple::x86_64: + for (auto ®: m_regs) + { + if ((strcmp(reg.name, "rip") == 0) || (strcmp(reg.name, "pc") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + else if ((strcmp(reg.name, "rsp") == 0) || (strcmp(reg.name, "sp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + else if ((strcmp(reg.name, "rbp") == 0) || (strcmp(reg.name, "fp") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + else if ((strcmp(reg.name, "rflags") == 0) || (strcmp(reg.name, "flags") == 0)) + reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + } + break; + + default: + break; + } + } } size_t diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.h b/source/Plugins/Process/Utility/DynamicRegisterInfo.h index a41c77e49f9d..1b99e2f1e701 100644 --- a/source/Plugins/Process/Utility/DynamicRegisterInfo.h +++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.h @@ -19,21 +19,21 @@ // Project includes #include "lldb/lldb-private.h" #include "lldb/Core/ConstString.h" +#include "lldb/Core/StructuredData.h" class DynamicRegisterInfo { public: DynamicRegisterInfo (); - DynamicRegisterInfo (const lldb_private::PythonDictionary &dict, - lldb::ByteOrder byte_order); - + DynamicRegisterInfo(const lldb_private::StructuredData::Dictionary &dict, + const lldb_private::ArchSpec &arch); + virtual ~DynamicRegisterInfo (); - size_t - SetRegisterInfo (const lldb_private::PythonDictionary &dict, - lldb::ByteOrder byte_order); + size_t SetRegisterInfo(const lldb_private::StructuredData::Dictionary &dict, + const lldb_private::ArchSpec &arch); void AddRegister (lldb_private::RegisterInfo ®_info, @@ -42,7 +42,7 @@ public: lldb_private::ConstString &set_name); void - Finalize (); + Finalize (const lldb_private::ArchSpec &arch); size_t GetNumRegisters() const; diff --git a/source/Plugins/Process/Utility/FreeBSDSignals.cpp b/source/Plugins/Process/Utility/FreeBSDSignals.cpp index b7c52aeb6d13..f082e143059c 100644 --- a/source/Plugins/Process/Utility/FreeBSDSignals.cpp +++ b/source/Plugins/Process/Utility/FreeBSDSignals.cpp @@ -24,8 +24,70 @@ FreeBSDSignals::Reset() { UnixSignals::Reset(); - // SIGNO NAME SHORT NAME SUPPRESS STOP NOTIFY DESCRIPTION - // ====== ============ ========== ======== ====== ====== =================================================== - AddSignal (32, "SIGTHR", "THR", false, true , true , "thread interrupt"); - AddSignal (33, "SIGLIBRT", "LIBRT", false, true , true , "reserved by real-time library"); + // SIGNO NAME SHORT NAME SUPPRESS STOP NOTIFY DESCRIPTION + // ====== ============ ========== ======== ====== ====== =================================================== + AddSignal (32, "SIGTHR", "THR", false, true , true , "thread interrupt"); + AddSignal (33, "SIGLIBRT", "LIBRT", false, true , true , "reserved by real-time library"); + AddSignal (65, "SIGRTMIN", "RTMIN", false, true , true , "real time signal 0"); + AddSignal (66, "SIGRTMIN+1", "RTMIN+1", false, true , true , "real time signal 1"); + AddSignal (67, "SIGRTMIN+2", "RTMIN+2", false, true , true , "real time signal 2"); + AddSignal (68, "SIGRTMIN+3", "RTMIN+3", false, true , true , "real time signal 3"); + AddSignal (69, "SIGRTMIN+4", "RTMIN+4", false, true , true , "real time signal 4"); + AddSignal (70, "SIGRTMIN+5", "RTMIN+5", false, true , true , "real time signal 5"); + AddSignal (71, "SIGRTMIN+6", "RTMIN+6", false, true , true , "real time signal 6"); + AddSignal (72, "SIGRTMIN+7", "RTMIN+7", false, true , true , "real time signal 7"); + AddSignal (73, "SIGRTMIN+8", "RTMIN+8", false, true , true , "real time signal 8"); + AddSignal (74, "SIGRTMIN+9", "RTMIN+9", false, true , true , "real time signal 9"); + AddSignal (75, "SIGRTMIN+10", "RTMIN+10", false, true , true , "real time signal 10"); + AddSignal (76, "SIGRTMIN+11", "RTMIN+11", false, true , true , "real time signal 11"); + AddSignal (77, "SIGRTMIN+12", "RTMIN+12", false, true , true , "real time signal 12"); + AddSignal (78, "SIGRTMIN+13", "RTMIN+13", false, true , true , "real time signal 13"); + AddSignal (79, "SIGRTMIN+14", "RTMIN+14", false, true , true , "real time signal 14"); + AddSignal (80, "SIGRTMIN+15", "RTMIN+15", false, true , true , "real time signal 15"); + AddSignal (81, "SIGRTMIN+16", "RTMIN+16", false, true , true , "real time signal 16"); + AddSignal (82, "SIGRTMIN+17", "RTMIN+17", false, true , true , "real time signal 17"); + AddSignal (83, "SIGRTMIN+18", "RTMIN+18", false, true , true , "real time signal 18"); + AddSignal (84, "SIGRTMIN+19", "RTMIN+19", false, true , true , "real time signal 19"); + AddSignal (85, "SIGRTMIN+20", "RTMIN+20", false, true , true , "real time signal 20"); + AddSignal (86, "SIGRTMIN+21", "RTMIN+21", false, true , true , "real time signal 21"); + AddSignal (87, "SIGRTMIN+22", "RTMIN+22", false, true , true , "real time signal 22"); + AddSignal (88, "SIGRTMIN+23", "RTMIN+23", false, true , true , "real time signal 23"); + AddSignal (89, "SIGRTMIN+24", "RTMIN+24", false, true , true , "real time signal 24"); + AddSignal (90, "SIGRTMIN+25", "RTMIN+25", false, true , true , "real time signal 25"); + AddSignal (91, "SIGRTMIN+26", "RTMIN+26", false, true , true , "real time signal 26"); + AddSignal (92, "SIGRTMIN+27", "RTMIN+27", false, true , true , "real time signal 27"); + AddSignal (93, "SIGRTMIN+28", "RTMIN+28", false, true , true , "real time signal 28"); + AddSignal (94, "SIGRTMIN+29", "RTMIN+29", false, true , true , "real time signal 29"); + AddSignal (95, "SIGRTMIN+30", "RTMIN+30", false, true , true , "real time signal 30"); + AddSignal (96, "SIGRTMAX-30", "RTMAX-30", false, true , true , "real time signal 31"); + AddSignal (97, "SIGRTMAX-29", "RTMAX-29", false, true , true , "real time signal 32"); + AddSignal (98, "SIGRTMAX-28", "RTMAX-28", false, true , true , "real time signal 33"); + AddSignal (99, "SIGRTMAX-27", "RTMAX-27", false, true , true , "real time signal 34"); + AddSignal (100, "SIGRTMAX-26", "RTMAX-26", false, true , true , "real time signal 35"); + AddSignal (101, "SIGRTMAX-25", "RTMAX-25", false, true , true , "real time signal 36"); + AddSignal (102, "SIGRTMAX-24", "RTMAX-24", false, true , true , "real time signal 37"); + AddSignal (103, "SIGRTMAX-23", "RTMAX-23", false, true , true , "real time signal 38"); + AddSignal (104, "SIGRTMAX-22", "RTMAX-22", false, true , true , "real time signal 39"); + AddSignal (105, "SIGRTMAX-21", "RTMAX-21", false, true , true , "real time signal 40"); + AddSignal (106, "SIGRTMAX-20", "RTMAX-20", false, true , true , "real time signal 41"); + AddSignal (107, "SIGRTMAX-19", "RTMAX-19", false, true , true , "real time signal 42"); + AddSignal (108, "SIGRTMAX-18", "RTMAX-18", false, true , true , "real time signal 43"); + AddSignal (109, "SIGRTMAX-17", "RTMAX-17", false, true , true , "real time signal 44"); + AddSignal (110, "SIGRTMAX-16", "RTMAX-16", false, true , true , "real time signal 45"); + AddSignal (111, "SIGRTMAX-15", "RTMAX-15", false, true , true , "real time signal 46"); + AddSignal (112, "SIGRTMAX-14", "RTMAX-14", false, true , true , "real time signal 47"); + AddSignal (113, "SIGRTMAX-13", "RTMAX-13", false, true , true , "real time signal 48"); + AddSignal (114, "SIGRTMAX-12", "RTMAX-12", false, true , true , "real time signal 49"); + AddSignal (115, "SIGRTMAX-11", "RTMAX-11", false, true , true , "real time signal 50"); + AddSignal (116, "SIGRTMAX-10", "RTMAX-10", false, true , true , "real time signal 51"); + AddSignal (117, "SIGRTMAX-9", "RTMAX-9", false, true , true , "real time signal 52"); + AddSignal (118, "SIGRTMAX-8", "RTMAX-8", false, true , true , "real time signal 53"); + AddSignal (119, "SIGRTMAX-7", "RTMAX-7", false, true , true , "real time signal 54"); + AddSignal (120, "SIGRTMAX-6", "RTMAX-6", false, true , true , "real time signal 55"); + AddSignal (121, "SIGRTMAX-5", "RTMAX-5", false, true , true , "real time signal 56"); + AddSignal (122, "SIGRTMAX-4", "RTMAX-4", false, true , true , "real time signal 57"); + AddSignal (123, "SIGRTMAX-3", "RTMAX-3", false, true , true , "real time signal 58"); + AddSignal (124, "SIGRTMAX-2", "RTMAX-2", false, true , true , "real time signal 59"); + AddSignal (125, "SIGRTMAX-1", "RTMAX-1", false, true , true , "real time signal 60"); + AddSignal (126, "SIGRTMAX", "RTMAX", false, true , true , "real time signal 61"); } diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index 7db83ae5467f..3923c5433406 100644 --- a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -14,6 +14,7 @@ #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPlanCallFunction.h" @@ -27,8 +28,6 @@ #define PROT_READ 1 #define PROT_WRITE 2 #define PROT_EXEC 4 -#define MAP_PRIVATE 2 -#define MAP_ANON 0x1000 #endif using namespace lldb; @@ -87,10 +86,8 @@ lldb_private::InferiorCallMmap (Process *process, prot_arg |= PROT_WRITE; } - if (flags & eMmapFlagsPrivate) - flags_arg |= MAP_PRIVATE; - if (flags & eMmapFlagsAnon) - flags_arg |= MAP_ANON; + const ArchSpec arch = process->GetTarget().GetArchitecture(); + flags_arg = process->GetTarget().GetPlatform()->ConvertMmapFlagsToPlatform(arch,flags); AddressRange mmap_range; if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, mmap_range)) diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.h b/source/Plugins/Process/Utility/InferiorCallPOSIX.h index d8b6d0ed57fd..e56e95c43773 100644 --- a/source/Plugins/Process/Utility/InferiorCallPOSIX.h +++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.h @@ -25,11 +25,6 @@ enum MmapProt { eMmapProtWrite = 4 }; -enum MmapFlags { - eMmapFlagsPrivate = 1, - eMmapFlagsAnon = 2 -}; - bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr, lldb::addr_t addr, lldb::addr_t length, unsigned prot, unsigned flags, lldb::addr_t fd, lldb::addr_t offset); diff --git a/source/Plugins/Process/Utility/LinuxSignals.cpp b/source/Plugins/Process/Utility/LinuxSignals.cpp index 11a3eef23529..0680d268233c 100644 --- a/source/Plugins/Process/Utility/LinuxSignals.cpp +++ b/source/Plugins/Process/Utility/LinuxSignals.cpp @@ -12,7 +12,7 @@ // Project includes #include "LinuxSignals.h" -using namespace process_linux; +using namespace lldb_private::process_linux; LinuxSignals::LinuxSignals() : UnixSignals() @@ -25,38 +25,71 @@ LinuxSignals::Reset() { m_signals.clear(); - AddSignal (1, "SIGHUP", "HUP", false, true , true , "hangup"); - AddSignal (2, "SIGINT", "INT", true , true , true , "interrupt"); - AddSignal (3, "SIGQUIT", "QUIT", false, true , true , "quit"); - AddSignal (4, "SIGILL", "ILL", false, true , true , "illegal instruction"); - AddSignal (5, "SIGTRAP", "TRAP", true , true , true , "trace trap (not reset when caught)"); - AddSignal (6, "SIGABRT", "ABRT", false, true , true , "abort()"); - AddSignal (6, "SIGIOT", "IOT", false, true , true , "IOT trap"); - AddSignal (7, "SIGBUS", "BUS", false, true , true , "bus error"); - AddSignal (8, "SIGFPE", "FPE", false, true , true , "floating point exception"); - AddSignal (9, "SIGKILL", "KILL", false, true , true , "kill"); - AddSignal (10, "SIGUSR1", "USR1", false, true , true , "user defined signal 1"); - AddSignal (11, "SIGSEGV", "SEGV", false, true , true , "segmentation violation"); - AddSignal (12, "SIGUSR2", "USR2", false, true , true , "user defined signal 2"); - AddSignal (13, "SIGPIPE", "PIPE", false, true , true , "write to pipe with reading end closed"); - AddSignal (14, "SIGALRM", "ALRM", false, false, false, "alarm"); - AddSignal (15, "SIGTERM", "TERM", false, true , true , "termination requested"); - AddSignal (16, "SIGSTKFLT", "STKFLT", false, true , true , "stack fault"); - AddSignal (16, "SIGCLD", "CLD", false, false, true , "same as SIGCHLD"); - AddSignal (17, "SIGCHLD", "CHLD", false, false, true , "child status has changed"); - AddSignal (18, "SIGCONT", "CONT", false, true , true , "process continue"); - AddSignal (19, "SIGSTOP", "STOP", true , true , true , "process stop"); - AddSignal (20, "SIGTSTP", "TSTP", false, true , true , "tty stop"); - AddSignal (21, "SIGTTIN", "TTIN", false, true , true , "background tty read"); - AddSignal (22, "SIGTTOU", "TTOU", false, true , true , "background tty write"); - AddSignal (23, "SIGURG", "URG", false, true , true , "urgent data on socket"); - AddSignal (24, "SIGXCPU", "XCPU", false, true , true , "CPU resource exceeded"); - AddSignal (25, "SIGXFSZ", "XFSZ", false, true , true , "file size limit exceeded"); - AddSignal (26, "SIGVTALRM", "VTALRM", false, true , true , "virtual time alarm"); - AddSignal (27, "SIGPROF", "PROF", false, false, false, "profiling time alarm"); - AddSignal (28, "SIGWINCH", "WINCH", false, true , true , "window size changes"); - AddSignal (29, "SIGPOLL", "POLL", false, true , true , "pollable event"); - AddSignal (29, "SIGIO", "IO", false, true , true , "input/output ready"); - AddSignal (30, "SIGPWR", "PWR", false, true , true , "power failure"); - AddSignal (31, "SIGSYS", "SYS", false, true , true , "invalid system call"); + AddSignal (1, "SIGHUP", "HUP", false, true , true , "hangup"); + AddSignal (2, "SIGINT", "INT", true , true , true , "interrupt"); + AddSignal (3, "SIGQUIT", "QUIT", false, true , true , "quit"); + AddSignal (4, "SIGILL", "ILL", false, true , true , "illegal instruction"); + AddSignal (5, "SIGTRAP", "TRAP", true , true , true , "trace trap (not reset when caught)"); + AddSignal (6, "SIGABRT", "ABRT", false, true , true , "abort()"); + AddSignal (6, "SIGIOT", "IOT", false, true , true , "IOT trap"); + AddSignal (7, "SIGBUS", "BUS", false, true , true , "bus error"); + AddSignal (8, "SIGFPE", "FPE", false, true , true , "floating point exception"); + AddSignal (9, "SIGKILL", "KILL", false, true , true , "kill"); + AddSignal (10, "SIGUSR1", "USR1", false, true , true , "user defined signal 1"); + AddSignal (11, "SIGSEGV", "SEGV", false, true , true , "segmentation violation"); + AddSignal (12, "SIGUSR2", "USR2", false, true , true , "user defined signal 2"); + AddSignal (13, "SIGPIPE", "PIPE", false, true , true , "write to pipe with reading end closed"); + AddSignal (14, "SIGALRM", "ALRM", false, false, false, "alarm"); + AddSignal (15, "SIGTERM", "TERM", false, true , true , "termination requested"); + AddSignal (16, "SIGSTKFLT", "STKFLT", false, true , true , "stack fault"); + AddSignal (16, "SIGCLD", "CLD", false, false, true , "same as SIGCHLD"); + AddSignal (17, "SIGCHLD", "CHLD", false, false, true , "child status has changed"); + AddSignal (18, "SIGCONT", "CONT", false, true , true , "process continue"); + AddSignal (19, "SIGSTOP", "STOP", true , true , true , "process stop"); + AddSignal (20, "SIGTSTP", "TSTP", false, true , true , "tty stop"); + AddSignal (21, "SIGTTIN", "TTIN", false, true , true , "background tty read"); + AddSignal (22, "SIGTTOU", "TTOU", false, true , true , "background tty write"); + AddSignal (23, "SIGURG", "URG", false, true , true , "urgent data on socket"); + AddSignal (24, "SIGXCPU", "XCPU", false, true , true , "CPU resource exceeded"); + AddSignal (25, "SIGXFSZ", "XFSZ", false, true , true , "file size limit exceeded"); + AddSignal (26, "SIGVTALRM", "VTALRM", false, true , true , "virtual time alarm"); + AddSignal (27, "SIGPROF", "PROF", false, false, false, "profiling time alarm"); + AddSignal (28, "SIGWINCH", "WINCH", false, true , true , "window size changes"); + AddSignal (29, "SIGPOLL", "POLL", false, true , true , "pollable event"); + AddSignal (29, "SIGIO", "IO", false, true , true , "input/output ready"); + AddSignal (30, "SIGPWR", "PWR", false, true , true , "power failure"); + AddSignal (31, "SIGSYS", "SYS", false, true , true , "invalid system call"); + AddSignal (32, "SIG32", "SIG32", false, true , true , "threading library internal signal 1"); + AddSignal (33, "SIG33", "SIG33", false, true , true , "threading library internal signal 2"); + AddSignal (34, "SIGRTMIN", "RTMIN", false, true , true , "real time signal 0"); + AddSignal (35, "SIGRTMIN+1", "RTMIN+1", false, true , true , "real time signal 1"); + AddSignal (36, "SIGRTMIN+2", "RTMIN+2", false, true , true , "real time signal 2"); + AddSignal (37, "SIGRTMIN+3", "RTMIN+3", false, true , true , "real time signal 3"); + AddSignal (38, "SIGRTMIN+4", "RTMIN+4", false, true , true , "real time signal 4"); + AddSignal (39, "SIGRTMIN+5", "RTMIN+5", false, true , true , "real time signal 5"); + AddSignal (40, "SIGRTMIN+6", "RTMIN+6", false, true , true , "real time signal 6"); + AddSignal (41, "SIGRTMIN+7", "RTMIN+7", false, true , true , "real time signal 7"); + AddSignal (42, "SIGRTMIN+8", "RTMIN+8", false, true , true , "real time signal 8"); + AddSignal (43, "SIGRTMIN+9", "RTMIN+9", false, true , true , "real time signal 9"); + AddSignal (44, "SIGRTMIN+10", "RTMIN+10", false, true , true , "real time signal 10"); + AddSignal (45, "SIGRTMIN+11", "RTMIN+11", false, true , true , "real time signal 11"); + AddSignal (46, "SIGRTMIN+12", "RTMIN+12", false, true , true , "real time signal 12"); + AddSignal (47, "SIGRTMIN+13", "RTMIN+13", false, true , true , "real time signal 13"); + AddSignal (48, "SIGRTMIN+14", "RTMIN+14", false, true , true , "real time signal 14"); + AddSignal (49, "SIGRTMIN+15", "RTMIN+15", false, true , true , "real time signal 15"); + AddSignal (50, "SIGRTMAX-14", "RTMAX-14", false, true , true , "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill -l" output + AddSignal (51, "SIGRTMAX-13", "RTMAX-13", false, true , true , "real time signal 17"); + AddSignal (52, "SIGRTMAX-12", "RTMAX-12", false, true , true , "real time signal 18"); + AddSignal (53, "SIGRTMAX-11", "RTMAX-11", false, true , true , "real time signal 19"); + AddSignal (54, "SIGRTMAX-10", "RTMAX-10", false, true , true , "real time signal 20"); + AddSignal (55, "SIGRTMAX-9", "RTMAX-9", false, true , true , "real time signal 21"); + AddSignal (56, "SIGRTMAX-8", "RTMAX-8", false, true , true , "real time signal 22"); + AddSignal (57, "SIGRTMAX-7", "RTMAX-7", false, true , true , "real time signal 23"); + AddSignal (58, "SIGRTMAX-6", "RTMAX-6", false, true , true , "real time signal 24"); + AddSignal (59, "SIGRTMAX-5", "RTMAX-5", false, true , true , "real time signal 25"); + AddSignal (60, "SIGRTMAX-4", "RTMAX-4", false, true , true , "real time signal 26"); + AddSignal (61, "SIGRTMAX-3", "RTMAX-3", false, true , true , "real time signal 27"); + AddSignal (62, "SIGRTMAX-2", "RTMAX-2", false, true , true , "real time signal 28"); + AddSignal (63, "SIGRTMAX-1", "RTMAX-1", false, true , true , "real time signal 29"); + AddSignal (64, "SIGRTMAX", "RTMAX", false, true , true , "real time signal 30"); } diff --git a/source/Plugins/Process/Utility/LinuxSignals.h b/source/Plugins/Process/Utility/LinuxSignals.h index 9645b3d8725a..5102bcb95e74 100644 --- a/source/Plugins/Process/Utility/LinuxSignals.h +++ b/source/Plugins/Process/Utility/LinuxSignals.h @@ -16,8 +16,8 @@ // Project includes #include "lldb/Target/UnixSignals.h" -namespace process_linux -{ +namespace lldb_private { +namespace process_linux { /// Linux specific set of Unix signals. class LinuxSignals @@ -30,6 +30,8 @@ namespace process_linux void Reset(); }; -} + +} // namespace lldb_private +} // namespace process_linux #endif diff --git a/source/Plugins/Process/Utility/MipsLinuxSignals.cpp b/source/Plugins/Process/Utility/MipsLinuxSignals.cpp new file mode 100644 index 000000000000..e4e654626eaa --- /dev/null +++ b/source/Plugins/Process/Utility/MipsLinuxSignals.cpp @@ -0,0 +1,95 @@ +//===-- MipsLinuxSignals.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "MipsLinuxSignals.h" + +using namespace lldb_private::process_linux; + +MipsLinuxSignals::MipsLinuxSignals() + : UnixSignals() +{ + Reset(); +} + +void +MipsLinuxSignals::Reset() +{ + m_signals.clear(); + + AddSignal (1, "SIGHUP", "HUP", false, true , true , "hangup"); + AddSignal (2, "SIGINT", "INT", true , true , true , "interrupt"); + AddSignal (3, "SIGQUIT", "QUIT", false, true , true , "quit"); + AddSignal (4, "SIGILL", "ILL", false, true , true , "illegal instruction"); + AddSignal (5, "SIGTRAP", "TRAP", true , true , true , "trace trap (not reset when caught)"); + AddSignal (6, "SIGABRT", "ABRT", false, true , true , "abort()"); + AddSignal (6, "SIGIOT", "IOT", false, true , true , "IOT trap"); + AddSignal (7, "SIGEMT", "EMT", false, true , true , "terminate process with core dump"); + AddSignal (8, "SIGFPE", "FPE", false, true , true , "floating point exception"); + AddSignal (9, "SIGKILL", "KILL", false, true , true , "kill"); + AddSignal (10, "SIGBUS", "BUS", false, true , true , "bus error"); + AddSignal (11, "SIGSEGV", "SEGV", false, true , true , "segmentation violation"); + AddSignal (12, "SIGSYS", "SYS", false, true , true , "invalid system call"); + AddSignal (13, "SIGPIPE", "PIPE", false, true , true , "write to pipe with reading end closed"); + AddSignal (14, "SIGALRM", "ALRM", false, false, false, "alarm"); + AddSignal (15, "SIGTERM", "TERM", false, true , true , "termination requested"); + AddSignal (16, "SIGUSR1", "USR1", false, true , true , "user defined signal 1"); + AddSignal (17, "SIGUSR2", "USR2", false, true , true , "user defined signal 2"); + AddSignal (18, "SIGCLD", "CLD", false, false, true , "same as SIGCHLD"); + AddSignal (18, "SIGCHLD", "CHLD", false, false, true , "child status has changed"); + AddSignal (19, "SIGPWR", "PWR", false, true , true , "power failure"); + AddSignal (20, "SIGWINCH", "WINCH", false, true , true , "window size changes"); + AddSignal (21, "SIGURG", "URG", false, true , true , "urgent data on socket"); + AddSignal (22, "SIGIO", "IO", false, true , true , "input/output ready"); + AddSignal (22, "SIGPOLL", "POLL", false, true , true , "pollable event"); + AddSignal (23, "SIGSTOP", "STOP", true , true , true , "process stop"); + AddSignal (24, "SIGTSTP", "TSTP", false, true , true , "tty stop"); + AddSignal (25, "SIGCONT", "CONT", false, true , true , "process continue"); + AddSignal (26, "SIGTTIN", "TTIN", false, true , true , "background tty read"); + AddSignal (27, "SIGTTOU", "TTOU", false, true , true , "background tty write"); + AddSignal (28, "SIGVTALRM", "VTALRM", false, true , true , "virtual time alarm"); + AddSignal (29, "SIGPROF", "PROF", false, false, false, "profiling time alarm"); + AddSignal (30, "SIGXCPU", "XCPU", false, true , true , "CPU resource exceeded"); + AddSignal (31, "SIGXFSZ", "XFSZ", false, true , true , "file size limit exceeded"); + AddSignal (32, "SIG32", "SIG32", false, true , true , "threading library internal signal 1"); + AddSignal (33, "SIG33", "SIG33", false, true , true , "threading library internal signal 2"); + AddSignal (34, "SIGRTMIN", "RTMIN", false, true , true , "real time signal 0"); + AddSignal (35, "SIGRTMIN+1", "RTMIN+1", false, true , true , "real time signal 1"); + AddSignal (36, "SIGRTMIN+2", "RTMIN+2", false, true , true , "real time signal 2"); + AddSignal (37, "SIGRTMIN+3", "RTMIN+3", false, true , true , "real time signal 3"); + AddSignal (38, "SIGRTMIN+4", "RTMIN+4", false, true , true , "real time signal 4"); + AddSignal (39, "SIGRTMIN+5", "RTMIN+5", false, true , true , "real time signal 5"); + AddSignal (40, "SIGRTMIN+6", "RTMIN+6", false, true , true , "real time signal 6"); + AddSignal (41, "SIGRTMIN+7", "RTMIN+7", false, true , true , "real time signal 7"); + AddSignal (42, "SIGRTMIN+8", "RTMIN+8", false, true , true , "real time signal 8"); + AddSignal (43, "SIGRTMIN+9", "RTMIN+9", false, true , true , "real time signal 9"); + AddSignal (44, "SIGRTMIN+10", "RTMIN+10", false, true , true , "real time signal 10"); + AddSignal (45, "SIGRTMIN+11", "RTMIN+11", false, true , true , "real time signal 11"); + AddSignal (46, "SIGRTMIN+12", "RTMIN+12", false, true , true , "real time signal 12"); + AddSignal (47, "SIGRTMIN+13", "RTMIN+13", false, true , true , "real time signal 13"); + AddSignal (48, "SIGRTMIN+14", "RTMIN+14", false, true , true , "real time signal 14"); + AddSignal (49, "SIGRTMIN+15", "RTMIN+15", false, true , true , "real time signal 15"); + AddSignal (50, "SIGRTMAX-14", "RTMAX-14", false, true , true , "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill -l" output + AddSignal (51, "SIGRTMAX-13", "RTMAX-13", false, true , true , "real time signal 17"); + AddSignal (52, "SIGRTMAX-12", "RTMAX-12", false, true , true , "real time signal 18"); + AddSignal (53, "SIGRTMAX-11", "RTMAX-11", false, true , true , "real time signal 19"); + AddSignal (54, "SIGRTMAX-10", "RTMAX-10", false, true , true , "real time signal 20"); + AddSignal (55, "SIGRTMAX-9", "RTMAX-9", false, true , true , "real time signal 21"); + AddSignal (56, "SIGRTMAX-8", "RTMAX-8", false, true , true , "real time signal 22"); + AddSignal (57, "SIGRTMAX-7", "RTMAX-7", false, true , true , "real time signal 23"); + AddSignal (58, "SIGRTMAX-6", "RTMAX-6", false, true , true , "real time signal 24"); + AddSignal (59, "SIGRTMAX-5", "RTMAX-5", false, true , true , "real time signal 25"); + AddSignal (60, "SIGRTMAX-4", "RTMAX-4", false, true , true , "real time signal 26"); + AddSignal (61, "SIGRTMAX-3", "RTMAX-3", false, true , true , "real time signal 27"); + AddSignal (62, "SIGRTMAX-2", "RTMAX-2", false, true , true , "real time signal 28"); + AddSignal (63, "SIGRTMAX-1", "RTMAX-1", false, true , true , "real time signal 29"); + AddSignal (64, "SIGRTMAX", "RTMAX", false, true , true , "real time signal 30"); +} diff --git a/source/Plugins/Process/Utility/MipsLinuxSignals.h b/source/Plugins/Process/Utility/MipsLinuxSignals.h new file mode 100644 index 000000000000..2e603fbbdf3c --- /dev/null +++ b/source/Plugins/Process/Utility/MipsLinuxSignals.h @@ -0,0 +1,37 @@ +//===-- MipsLinuxSignals.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MipsLinuxSignals_H_ +#define liblldb_MipsLinuxSignals_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/UnixSignals.h" + +namespace lldb_private { +namespace process_linux { + + /// Linux specific set of Unix signals. + class MipsLinuxSignals + : public lldb_private::UnixSignals + { + public: + MipsLinuxSignals(); + + private: + void + Reset(); + }; + +} // namespace lldb_private +} // namespace process_linux + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm.cpp b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm.cpp new file mode 100644 index 000000000000..8005a6339f6d --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm.cpp @@ -0,0 +1,88 @@ +//===-- RegisterContextFreeBSD_arm.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include <stddef.h> +#include <vector> +#include <cassert> + +#include "llvm/Support/Compiler.h" +#include "lldb/lldb-defines.h" + +#include "RegisterContextFreeBSD_arm.h" + +using namespace lldb; +using namespace lldb_private; + +// Based on RegisterContextLinux_arm.cpp and +// http://svnweb.freebsd.org/base/head/sys/arm/include/reg.h +#define GPR_OFFSET(idx) ((idx) * 4) +#define FPU_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextFreeBSD_arm::GPR)) +#define EXC_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextFreeBSD_arm::GPR) + sizeof (RegisterContextFreeBSD_arm::FPU)) +#define DBG_OFFSET(reg) ((LLVM_EXTENSION offsetof (RegisterContextFreeBSD_arm::DBG, reg) + sizeof (RegisterContextFreeBSD_arm::GPR) + sizeof (RegisterContextFreeBSD_arm::FPU) + sizeof (RegisterContextFreeBSD_arm::EXC))) + +#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextFreeBSD_arm::DBG *)NULL)->reg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, dbg_##reg##i }, NULL, NULL +#define REG_CONTEXT_SIZE (sizeof (RegisterContextFreeBSD_arm::GPR) + sizeof (RegisterContextFreeBSD_arm::FPU) + sizeof (RegisterContextFreeBSD_arm::EXC)) + +//----------------------------------------------------------------------------- +// Include RegisterInfos_arm to declare our g_register_infos_arm structure. +//----------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_ARM_STRUCT +#include "RegisterInfos_arm.h" +#undef DECLARE_REGISTER_INFOS_ARM_STRUCT + +static const lldb_private::RegisterInfo * +GetRegisterInfoPtr (const lldb_private::ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::arm: + return g_register_infos_arm; + default: + assert(false && "Unhandled target architecture."); + return NULL; + } +} + +static uint32_t +GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::arm: + return static_cast<uint32_t>(sizeof(g_register_infos_arm) / sizeof(g_register_infos_arm[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextFreeBSD_arm::RegisterContextFreeBSD_arm(const lldb_private::ArchSpec &target_arch) : + lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) +{ +} + +size_t +RegisterContextFreeBSD_arm::GetGPRSize() const +{ + return sizeof(struct RegisterContextFreeBSD_arm::GPR); +} + +const lldb_private::RegisterInfo * +RegisterContextFreeBSD_arm::GetRegisterInfo() const +{ + return m_register_info_p; +} + +uint32_t +RegisterContextFreeBSD_arm::GetRegisterCount() const +{ + return m_register_info_count; +} diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm.h b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm.h new file mode 100644 index 000000000000..c4287e9f0a47 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm.h @@ -0,0 +1,75 @@ +//===-- RegisterContextFreeBSD_arm.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextFreeBSD_arm_h_ +#define liblldb_RegisterContextFreeBSD_arm_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" +#include "RegisterContextPOSIX.h" +#include "RegisterInfoInterface.h" + +class RegisterContextFreeBSD_arm + : public lldb_private::RegisterInfoInterface +{ +public: + + struct GPR + { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + + struct QReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + struct EXC + { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG + { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + RegisterContextFreeBSD_arm(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount () const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif // liblldb_RegisterContextFreeBSD_arm_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm64.cpp b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm64.cpp new file mode 100644 index 000000000000..d39a9da5714b --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm64.cpp @@ -0,0 +1,86 @@ +//===-- RegisterContextFreeBSD_arm64.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include <vector> +#include "RegisterContextPOSIX_arm64.h" +#include "RegisterContextFreeBSD_arm64.h" + +using namespace lldb; + +// Based on RegisterContextDarwin_arm64.cpp +#define GPR_OFFSET(idx) ((idx) * 8) +#define GPR_OFFSET_NAME(reg) (LLVM_EXTENSION offsetof (RegisterContextFreeBSD_arm64::GPR, reg)) + +#define FPU_OFFSET(idx) ((idx) * 16 + sizeof (RegisterContextFreeBSD_arm64::GPR)) +#define FPU_OFFSET_NAME(reg) (LLVM_EXTENSION offsetof (RegisterContextFreeBSD_arm64::FPU, reg)) + +#define EXC_OFFSET_NAME(reg) (LLVM_EXTENSION offsetof (RegisterContextFreeBSD_arm64::EXC, reg) + sizeof (RegisterContextFreeBSD_arm64::GPR) + sizeof (RegisterContextFreeBSD_arm64::FPU)) +#define DBG_OFFSET_NAME(reg) (LLVM_EXTENSION offsetof (RegisterContextFreeBSD_arm64::DBG, reg) + sizeof (RegisterContextFreeBSD_arm64::GPR) + sizeof (RegisterContextFreeBSD_arm64::FPU) + sizeof (RegisterContextFreeBSD_arm64::EXC)) + +#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextFreeBSD_arm64::DBG *)NULL)->reg[i]), DBG_OFFSET_NAME(reg[i]), lldb::eEncodingUint, lldb::eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, dbg_##reg##i }, NULL, NULL +#define REG_CONTEXT_SIZE (sizeof (RegisterContextFreeBSD_arm64::GPR) + sizeof (RegisterContextFreeBSD_arm64::FPU) + sizeof (RegisterContextFreeBSD_arm64::EXC)) + +//----------------------------------------------------------------------------- +// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure. +//----------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_ARM64_STRUCT +#include "RegisterInfos_arm64.h" +#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT + +static const lldb_private::RegisterInfo * +GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::aarch64: + return g_register_infos_arm64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::aarch64: + return static_cast<uint32_t>(sizeof(g_register_infos_arm64) / sizeof(g_register_infos_arm64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextFreeBSD_arm64::RegisterContextFreeBSD_arm64(const lldb_private::ArchSpec &target_arch) : + RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) +{ +} + +size_t +RegisterContextFreeBSD_arm64::GetGPRSize() const +{ + return sizeof(struct RegisterContextFreeBSD_arm64::GPR); +} + +const lldb_private::RegisterInfo * +RegisterContextFreeBSD_arm64::GetRegisterInfo() const +{ + return m_register_info_p; +} + +uint32_t +RegisterContextFreeBSD_arm64::GetRegisterCount() const +{ + return m_register_info_count; +} + diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm64.h b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm64.h new file mode 100644 index 000000000000..249027aaa76c --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_arm64.h @@ -0,0 +1,78 @@ +//===-- RegisterContextFreeBSD_arm64.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextFreeBSD_arm64_H_ +#define liblldb_RegisterContextFreeBSD_arm64_H_ + +#include "RegisterContextPOSIX.h" + +class RegisterContextFreeBSD_arm64: + public lldb_private::RegisterInfoInterface +{ +public: + // based on RegisterContextDarwin_arm64.h + struct GPR + { + uint64_t x[29]; // x0-x28 + uint64_t fp; // x29 + uint64_t lr; // x30 + uint64_t sp; // x31 + uint64_t pc; // pc + uint32_t cpsr; // cpsr + }; + + // based on RegisterContextDarwin_arm64.h + struct VReg + { + uint8_t bytes[16]; + }; + + // based on RegisterContextDarwin_arm64.h + struct FPU + { + VReg v[32]; + uint32_t fpsr; + uint32_t fpcr; + }; + + // based on RegisterContextDarwin_arm64.h + struct EXC + { + uint64_t far; // Virtual Fault Address + uint32_t esr; // Exception syndrome + uint32_t exception; // number of arm exception token + }; + + // based on RegisterContextDarwin_arm64.h + struct DBG + { + uint64_t bvr[16]; + uint64_t bcr[16]; + uint64_t wvr[16]; + uint64_t wcr[16]; + uint64_t mdscr_el1; + }; + + RegisterContextFreeBSD_arm64(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount () const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp b/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp index 185ba26944fe..0171da66a199 100644 --- a/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp @@ -45,6 +45,13 @@ struct dbreg { /* Index 7: debug control */ }; +using FPR_i386 = FXSAVE; + +struct UserArea +{ + GPR gpr; + FPR_i386 i387; +}; #define DR_SIZE sizeof(uint32_t) #define DR_OFFSET(reg_index) \ diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp index 257de7198590..34f2d185da8a 100644 --- a/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp @@ -46,19 +46,24 @@ typedef struct _GPR uint64_t ss; } GPR; -struct dbreg { - uint64_t dr[16]; /* debug registers */ - /* Index 0-3: debug address registers */ - /* Index 4-5: reserved */ - /* Index 6: debug status */ - /* Index 7: debug control */ - /* Index 8-15: reserved */ +struct DBG { + uint64_t dr[16]; /* debug registers */ + /* Index 0-3: debug address registers */ + /* Index 4-5: reserved */ + /* Index 6: debug status */ + /* Index 7: debug control */ + /* Index 8-15: reserved */ }; -#define DR_SIZE sizeof(uint64_t) -#define DR_OFFSET(reg_index) \ - (LLVM_EXTENSION offsetof(dbreg, dr[reg_index])) +struct UserArea +{ + GPR gpr; + FPR fpr; + DBG dbg; +}; +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(DBG, dr[reg_index])) //--------------------------------------------------------------------------- // Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64 structure. diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index 1b3a9491d1c8..37b007cfffcf 100644 --- a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -38,6 +38,15 @@ using namespace lldb; using namespace lldb_private; +static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) +{ + if (sym_ctx.symbol) + return sym_ctx.symbol->GetName(); + else if (sym_ctx.function) + return sym_ctx.function->GetName(); + return ConstString(); +} + RegisterContextLLDB::RegisterContextLLDB ( Thread& thread, @@ -175,12 +184,12 @@ RegisterContextLLDB::InitializeZerothFrame() if (m_sym_ctx.symbol) { UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'", - current_pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else if (m_sym_ctx.function) { UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'", - current_pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.function->GetName().AsCString()); + current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else { @@ -457,12 +466,12 @@ RegisterContextLLDB::InitializeNonZerothFrame() if (m_sym_ctx.symbol) { UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'", - pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else if (m_sym_ctx.function) { UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'", - pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.function->GetName().AsCString()); + pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else { @@ -500,7 +509,7 @@ RegisterContextLLDB::InitializeNonZerothFrame() if (decr_pc_and_recompute_addr_range) { UnwindLogMsg ("Backing up the pc value of 0x%" PRIx64 " by 1 and re-doing symbol lookup; old symbol was %s", - pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); Address temporary_pc; temporary_pc.SetLoadAddress (pc - 1, &process->GetTarget()); m_sym_ctx.Clear (false); @@ -514,7 +523,7 @@ RegisterContextLLDB::InitializeNonZerothFrame() if (m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range)) m_sym_ctx_valid = true; } - UnwindLogMsg ("Symbol is now %s", m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + UnwindLogMsg ("Symbol is now %s", GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } // If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function. @@ -599,7 +608,7 @@ RegisterContextLLDB::InitializeNonZerothFrame() if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa)) { - UnwindLogMsg ("failed to get cfa reg %d/%d", row_register_kind, active_row->GetCFARegister()); + UnwindLogMsg ("failed to get cfa"); m_frame_type = eNotAValidFrame; return; } @@ -683,7 +692,7 @@ RegisterContextLLDB::GetFastUnwindPlanForFrame () if (m_frame_type == eTrapHandlerFrame || m_frame_type == eDebuggerFrame) return unwind_plan_sp; - unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (m_thread); + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (*m_thread.CalculateTarget(), m_thread); if (unwind_plan_sp) { if (unwind_plan_sp->PlanValidAtAddress (m_current_pc)) @@ -846,12 +855,26 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () { if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { - // We probably have an UnwindPlan created by inspecting assembly instructions, and we probably - // don't have any eh_frame instructions available. - // The assembly profilers work really well with compiler-generated functions but hand-written - // assembly can be problematic. We'll set the architecture default UnwindPlan as our fallback - // UnwindPlan in case this doesn't work out when we try to unwind. - m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; + // We probably have an UnwindPlan created by inspecting assembly instructions. The + // assembly profilers work really well with compiler-generated functions but hand- + // written assembly can be problematic. We set the eh_frame based unwind plan as our + // fallback unwind plan if instruction emulation doesn't work out even for non call + // sites if it is available and use the architecture default unwind plan if it is + // not available. The eh_frame unwind plan is more reliable even on non call sites + // then the architecture default plan and for hand written assembly code it is often + // written in a way that it valid at all location what helps in the most common + // cases when the instruction emulation fails. + UnwindPlanSP eh_frame_unwind_plan = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one); + if (eh_frame_unwind_plan && + eh_frame_unwind_plan.get() != unwind_plan_sp.get() && + eh_frame_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName()) + { + m_fallback_unwind_plan_sp = eh_frame_unwind_plan; + } + else + { + m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; + } } UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; @@ -878,12 +901,25 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () } if (unwind_plan_sp && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { - // We probably have an UnwindPlan created by inspecting assembly instructions, and we probably - // don't have any eh_frame instructions available. - // The assembly profilers work really well with compiler-generated functions but hand-written - // assembly can be problematic. We'll set the architecture default UnwindPlan as our fallback - // UnwindPlan in case this doesn't work out when we try to unwind. - m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; + // We probably have an UnwindPlan created by inspecting assembly instructions. The assembly + // profilers work really well with compiler-generated functions but hand- written assembly + // can be problematic. We set the eh_frame based unwind plan as our fallback unwind plan if + // instruction emulation doesn't work out even for non call sites if it is available and use + // the architecture default unwind plan if it is not available. The eh_frame unwind plan is + // more reliable even on non call sites then the architecture default plan and for hand + // written assembly code it is often written in a way that it valid at all location what + // helps in the most common cases when the instruction emulation fails. + UnwindPlanSP eh_frame_unwind_plan = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one); + if (eh_frame_unwind_plan && + eh_frame_unwind_plan.get() != unwind_plan_sp.get() && + eh_frame_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName()) + { + m_fallback_unwind_plan_sp = eh_frame_unwind_plan; + } + else + { + m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; + } } if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) @@ -1394,16 +1430,13 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat if (unwindplan_regloc.IsSame()) { - if (IsFrameZero ()) - { - UnwindLogMsg ("could not supply caller's %s (%d) location, IsSame", - regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); - return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; - } - else - { - return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; - } + regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; + regloc.location.register_number = regnum.GetAsKind (eRegisterKindLLDB); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d), saved in register %s (%d)", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsCFAPlusOffset()) @@ -1577,7 +1610,7 @@ RegisterContextLLDB::TryFallbackUnwindPlan () UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); - if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM) + if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { addr_t new_cfa; if (!ReadCFAValueForRow (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) @@ -1654,7 +1687,7 @@ RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan () UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); - if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM) + if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { addr_t new_cfa; if (!ReadCFAValueForRow (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) @@ -1683,57 +1716,90 @@ RegisterContextLLDB::ReadCFAValueForRow (lldb::RegisterKind row_register_kind, const UnwindPlan::RowSP &row, addr_t &cfa_value) { - RegisterNumber cfa_reg (m_thread, row_register_kind, row->GetCFARegister()); RegisterValue reg_value; cfa_value = LLDB_INVALID_ADDRESS; addr_t cfa_reg_contents; - if (ReadGPRValue (cfa_reg, cfa_reg_contents)) + switch (row->GetCFAValue().GetValueType()) { - if (row->GetCFAType() == UnwindPlan::Row::CFAIsRegisterDereferenced) + case UnwindPlan::Row::CFAValue::isRegisterDereferenced: { - const RegisterInfo *reg_info = GetRegisterInfoAtIndex (cfa_reg.GetAsKind (eRegisterKindLLDB)); - RegisterValue reg_value; - if (reg_info) + RegisterNumber cfa_reg (m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber()); + if (ReadGPRValue (cfa_reg, cfa_reg_contents)) { - Error error = ReadRegisterValueFromMemory(reg_info, - cfa_reg_contents, - reg_info->byte_size, - reg_value); - if (error.Success ()) + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (cfa_reg.GetAsKind (eRegisterKindLLDB)); + RegisterValue reg_value; + if (reg_info) { - cfa_value = reg_value.GetAsUInt64(); - UnwindLogMsg ("CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64, - cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), - cfa_reg_contents, cfa_value); - return true; + Error error = ReadRegisterValueFromMemory(reg_info, + cfa_reg_contents, + reg_info->byte_size, + reg_value); + if (error.Success ()) + { + cfa_value = reg_value.GetAsUInt64(); + UnwindLogMsg ("CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64, + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents, cfa_value); + return true; + } + else + { + UnwindLogMsg ("Tried to deref reg %s (%d) [0x%" PRIx64 "] but memory read failed.", + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents); + } } - else + } + break; + } + case UnwindPlan::Row::CFAValue::isRegisterPlusOffset: + { + RegisterNumber cfa_reg (m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber()); + if (ReadGPRValue (cfa_reg, cfa_reg_contents)) + { + if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1) { - UnwindLogMsg ("Tried to deref reg %s (%d) [0x%" PRIx64 "] but memory read failed.", + UnwindLogMsg ("Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), cfa_reg_contents); + cfa_reg_contents = LLDB_INVALID_ADDRESS; + return false; } + cfa_value = cfa_reg_contents + row->GetCFAValue().GetOffset(); + UnwindLogMsg ("CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d", + cfa_value, + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents, row->GetCFAValue().GetOffset()); + return true; } + break; } - else + case UnwindPlan::Row::CFAValue::isDWARFExpression: { - if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1) + ExecutionContext exe_ctx(m_thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + DataExtractor dwarfdata (row->GetCFAValue().GetDWARFExpressionBytes(), + row->GetCFAValue().GetDWARFExpressionLength(), + process->GetByteOrder(), process->GetAddressByteSize()); + ModuleSP opcode_ctx; + DWARFExpression dwarfexpr (opcode_ctx, dwarfdata, 0, row->GetCFAValue().GetDWARFExpressionLength()); + dwarfexpr.SetRegisterKind (row_register_kind); + Value result; + Error error; + if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, this, 0, NULL, result, &error)) { - UnwindLogMsg ("Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64, - cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), - cfa_reg_contents); - cfa_reg_contents = LLDB_INVALID_ADDRESS; - return false; + cfa_value = result.GetScalar().ULongLong(); + + UnwindLogMsg ("CFA value set by DWARF expression is 0x%" PRIx64, cfa_value); + return true; } - cfa_value = cfa_reg_contents + row->GetCFAOffset (); - UnwindLogMsg ("CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d", - cfa_value, - cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), - cfa_reg_contents, row->GetCFAOffset ()); - return true; + UnwindLogMsg ("Failed to set CFA value via DWARF expression: %s", error.AsCString()); + break; } + default: + return false; } return false; } diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_arm.cpp b/source/Plugins/Process/Utility/RegisterContextLinux_arm.cpp new file mode 100644 index 000000000000..e7784b1712c3 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_arm.cpp @@ -0,0 +1,87 @@ +//===-- RegisterContextLinux_arm.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include <stddef.h> +#include <vector> +#include <cassert> + +#include "llvm/Support/Compiler.h" +#include "lldb/lldb-defines.h" + +#include "RegisterContextLinux_arm.h" + +using namespace lldb; +using namespace lldb_private; + +// Based on RegisterContextDarwin_arm.cpp +#define GPR_OFFSET(idx) ((idx) * 4) +#define FPU_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextLinux_arm::GPR)) +#define EXC_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextLinux_arm::GPR) + sizeof (RegisterContextLinux_arm::FPU)) +#define DBG_OFFSET(reg) ((LLVM_EXTENSION offsetof (RegisterContextLinux_arm::DBG, reg) + sizeof (RegisterContextLinux_arm::GPR) + sizeof (RegisterContextLinux_arm::FPU) + sizeof (RegisterContextLinux_arm::EXC))) + +#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextLinux_arm::DBG *)NULL)->reg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, dbg_##reg##i }, NULL, NULL +#define REG_CONTEXT_SIZE (sizeof (RegisterContextLinux_arm::GPR) + sizeof (RegisterContextLinux_arm::FPU) + sizeof (RegisterContextLinux_arm::EXC)) + +//----------------------------------------------------------------------------- +// Include RegisterInfos_arm to declare our g_register_infos_arm structure. +//----------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_ARM_STRUCT +#include "RegisterInfos_arm.h" +#undef DECLARE_REGISTER_INFOS_ARM_STRUCT + +static const lldb_private::RegisterInfo * +GetRegisterInfoPtr (const lldb_private::ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::arm: + return g_register_infos_arm; + default: + assert(false && "Unhandled target architecture."); + return NULL; + } +} + +static uint32_t +GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::arm: + return static_cast<uint32_t>(sizeof(g_register_infos_arm) / sizeof(g_register_infos_arm[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextLinux_arm::RegisterContextLinux_arm(const lldb_private::ArchSpec &target_arch) : + lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) +{ +} + +size_t +RegisterContextLinux_arm::GetGPRSize() const +{ + return sizeof(struct RegisterContextLinux_arm::GPR); +} + +const lldb_private::RegisterInfo * +RegisterContextLinux_arm::GetRegisterInfo() const +{ + return m_register_info_p; +} + +uint32_t +RegisterContextLinux_arm::GetRegisterCount() const +{ + return m_register_info_count; +} diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_arm.h b/source/Plugins/Process/Utility/RegisterContextLinux_arm.h new file mode 100644 index 000000000000..7087eb4c3dcc --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_arm.h @@ -0,0 +1,76 @@ +//===-- RegisterContextLinux_arm.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextLinux_arm_h_ +#define liblldb_RegisterContextLinux_arm_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" +#include "RegisterContextPOSIX.h" +#include "RegisterInfoInterface.h" + +class RegisterContextLinux_arm + : public lldb_private::RegisterInfoInterface +{ +public: + + struct GPR + { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + + struct QReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + struct EXC + { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG + { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + RegisterContextLinux_arm(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount () const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif // liblldb_RegisterContextLinux_arm_h_ + diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp b/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp index b38d6cc60cac..4f6bbc8f8ab8 100644 --- a/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp +++ b/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp @@ -66,7 +66,7 @@ struct UserArea { GPR regs; // General purpose registers. int32_t fpvalid; // True if FPU is being used. - FPR_i386 i387; // FPU registers. + FPR_i386 i387; // FPU registers. uint32_t tsize; // Text segment size. uint32_t dsize; // Data segment size. uint32_t ssize; // Stack segment size. @@ -112,6 +112,7 @@ RegisterContextLinux_i386::GetRegisterInfo() const switch (m_target_arch.GetMachine()) { case llvm::Triple::x86: + case llvm::Triple::x86_64: return g_register_infos_i386; default: assert(false && "Unhandled target architecture."); diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp b/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp new file mode 100644 index 000000000000..948437f51280 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp @@ -0,0 +1,102 @@ +//===-- RegisterContextLinux_mips.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include <vector> +#include <stddef.h> + +// For GDB, GCC and DWARF Register numbers +#include "RegisterContextLinux_mips.h" + +// Internal codes for mips registers +#include "lldb-mips64-register-enums.h" +#include "RegisterContext_mips64.h" + +using namespace lldb_private; +using namespace lldb; + +// GP registers +typedef struct _GPR +{ + uint32_t zero; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; + uint32_t r15; + uint32_t r16; + uint32_t r17; + uint32_t r18; + uint32_t r19; + uint32_t r20; + uint32_t r21; + uint32_t r22; + uint32_t r23; + uint32_t r24; + uint32_t r25; + uint32_t r26; + uint32_t r27; + uint32_t gp; + uint32_t sp; + uint32_t r30; + uint32_t ra; + uint32_t mullo; + uint32_t mulhi; + uint32_t pc; + uint32_t badvaddr; + uint32_t sr; + uint32_t cause; +} GPR; + +//--------------------------------------------------------------------------- +// Include RegisterInfos_mips to declare our g_register_infos_mips structure. +//--------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_MIPS_STRUCT +#include "RegisterInfos_mips.h" +#undef DECLARE_REGISTER_INFOS_MIPS_STRUCT + +RegisterContextLinux_mips::RegisterContextLinux_mips(const ArchSpec &target_arch) : + RegisterInfoInterface(target_arch) +{ +} + +size_t +RegisterContextLinux_mips::GetGPRSize() const +{ + return sizeof(GPR); +} + +const RegisterInfo * +RegisterContextLinux_mips::GetRegisterInfo() const +{ + switch (m_target_arch.GetMachine()) + { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + return g_register_infos_mips; + default: + assert(false && "Unhandled target architecture."); + return NULL; + } +} + +uint32_t +RegisterContextLinux_mips::GetRegisterCount () const +{ + return static_cast<uint32_t> (sizeof (g_register_infos_mips) / sizeof (g_register_infos_mips [0])); +} diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_mips.h b/source/Plugins/Process/Utility/RegisterContextLinux_mips.h new file mode 100644 index 000000000000..815473e76ddb --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_mips.h @@ -0,0 +1,32 @@ +//===-- RegisterContextLinux_mips.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextLinux_mips_H_ +#define liblldb_RegisterContextLinux_mips_H_ + +#include "lldb/lldb-private.h" +#include "RegisterInfoInterface.h" + +class RegisterContextLinux_mips + : public lldb_private::RegisterInfoInterface +{ +public: + RegisterContextLinux_mips(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount () const override; +}; + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp b/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp new file mode 100644 index 000000000000..a762930e8eb4 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp @@ -0,0 +1,143 @@ +//===-- RegisterContextLinux_mips64.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#if defined (__mips__) + +#include <vector> +#include <stddef.h> + +// For GDB, GCC and DWARF Register numbers +#include "RegisterContextLinux_mips64.h" + +// Internal codes for all mips64 registers +#include "lldb-mips64-register-enums.h" +#include "RegisterContext_mips64.h" + +using namespace lldb; +using namespace lldb_private; + +// GP registers +typedef struct _GPR +{ + uint64_t zero; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t gp; + uint64_t sp; + uint64_t r30; + uint64_t ra; + uint64_t mullo; + uint64_t mulhi; + uint64_t pc; + uint64_t badvaddr; + uint64_t sr; + uint64_t cause; + uint64_t ic; + uint64_t dummy; +} GPR; + +//--------------------------------------------------------------------------- +// Include RegisterInfos_mips64 to declare our g_register_infos_mips64 structure. +//--------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_MIPS64_STRUCT +#include "RegisterInfos_mips64.h" +#undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT + +//--------------------------------------------------------------------------- +// Include RegisterInfos_mips to declare our g_register_infos_mips structure. +//--------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_MIPS_STRUCT +#include "RegisterInfos_mips.h" +#undef DECLARE_REGISTER_INFOS_MIPS_STRUCT + +static const RegisterInfo * +GetRegisterInfoPtr (const ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return g_register_infos_mips64; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + return g_register_infos_mips; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +GetRegisterInfoCount (const ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return static_cast<uint32_t> (sizeof (g_register_infos_mips64) / sizeof (g_register_infos_mips64 [0])); + case llvm::Triple::mips: + case llvm::Triple::mipsel: + return static_cast<uint32_t> (sizeof (g_register_infos_mips) / sizeof (g_register_infos_mips [0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextLinux_mips64::RegisterContextLinux_mips64(const ArchSpec &target_arch) : + lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p (GetRegisterInfoPtr (target_arch)), + m_register_info_count (GetRegisterInfoCount (target_arch)) +{ +} + +size_t +RegisterContextLinux_mips64::GetGPRSize() const +{ + return sizeof(GPR); +} + +const RegisterInfo * +RegisterContextLinux_mips64::GetRegisterInfo() const +{ + return m_register_info_p; +} + +uint32_t +RegisterContextLinux_mips64::GetRegisterCount () const +{ + return m_register_info_count; +} + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h b/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h new file mode 100644 index 000000000000..546d148eb693 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h @@ -0,0 +1,40 @@ +//===-- RegisterContextLinux_mips64.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined (__mips__) + +#ifndef liblldb_RegisterContextLinux_mips64_H_ +#define liblldb_RegisterContextLinux_mips64_H_ + +#include "lldb/lldb-private.h" +#include "RegisterInfoInterface.h" + +class RegisterContextLinux_mips64 + : public lldb_private::RegisterInfoInterface +{ +public: + RegisterContextLinux_mips64(const lldb_private::ArchSpec &target_arch); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount () const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp index 5c93ebf88faa..c0993b47a126 100644 --- a/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp +++ b/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp @@ -46,12 +46,16 @@ typedef struct _GPR uint64_t gs; } GPR; +struct DBG { + uint64_t dr[8]; +}; + struct UserArea { GPR gpr; // General purpose registers. int32_t fpvalid; // True if FPU is being used. int32_t pad0; - FXSAVE i387; // General purpose floating point registers (see FPR for extended register sets). + FXSAVE fpr; // General purpose floating point registers (see FPR for extended register sets). uint64_t tsize; // Text segment size. uint64_t dsize; // Data segment size. uint64_t ssize; // Stack segment size. @@ -64,14 +68,15 @@ struct UserArea FXSAVE* fpstate; // Location of FPR's. uint64_t magic; // Identifier for core dumps. char u_comm[32]; // Command causing core dump. - uint64_t u_debugreg[8]; // Debug registers (DR0 - DR7). + DBG dbg; // Debug registers. uint64_t error_code; // CPU error code. uint64_t fault_address; // Control register CR3. }; -#define DR_SIZE sizeof(((UserArea*)NULL)->u_debugreg[0]) + #define DR_OFFSET(reg_index) \ - (LLVM_EXTENSION offsetof(UserArea, u_debugreg[reg_index])) + (LLVM_EXTENSION offsetof(UserArea, dbg) + \ + LLVM_EXTENSION offsetof(DBG, dr[reg_index])) //--------------------------------------------------------------------------- // Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64 structure. diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp new file mode 100644 index 000000000000..d306f86256bc --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp @@ -0,0 +1,287 @@ +//===-- RegisterContextPOSIX_arm.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstring> +#include <errno.h> +#include <stdint.h> + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_arm.h" +#include "Plugins/Process/elf-core/ProcessElfCore.h" + +using namespace lldb; +using namespace lldb_private; + +// arm general purpose registers. +const uint32_t g_gpr_regnums_arm[] = +{ + gpr_r0_arm, + gpr_r1_arm, + gpr_r2_arm, + gpr_r3_arm, + gpr_r4_arm, + gpr_r5_arm, + gpr_r6_arm, + gpr_r7_arm, + gpr_r8_arm, + gpr_r9_arm, + gpr_r10_arm, + gpr_r11_arm, + gpr_r12_arm, + gpr_sp_arm, + gpr_lr_arm, + gpr_pc_arm, + gpr_cpsr_arm, + LLDB_INVALID_REGNUM // register sets need to end with this flag + +}; +static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) == k_num_gpr_registers_arm, \ + "g_gpr_regnums_arm has wrong number of register infos"); + +// arm floating point registers. +static const uint32_t g_fpu_regnums_arm[] = +{ + fpu_s0_arm, + fpu_s1_arm, + fpu_s2_arm, + fpu_s3_arm, + fpu_s4_arm, + fpu_s5_arm, + fpu_s6_arm, + fpu_s7_arm, + fpu_s8_arm, + fpu_s9_arm, + fpu_s10_arm, + fpu_s11_arm, + fpu_s12_arm, + fpu_s13_arm, + fpu_s14_arm, + fpu_s15_arm, + fpu_s16_arm, + fpu_s17_arm, + fpu_s18_arm, + fpu_s19_arm, + fpu_s20_arm, + fpu_s21_arm, + fpu_s22_arm, + fpu_s23_arm, + fpu_s24_arm, + fpu_s25_arm, + fpu_s26_arm, + fpu_s27_arm, + fpu_s28_arm, + fpu_s29_arm, + fpu_s30_arm, + fpu_s31_arm, + fpu_fpscr_arm, + LLDB_INVALID_REGNUM // register sets need to end with this flag + +}; +static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) == k_num_fpr_registers_arm, \ + "g_fpu_regnums_arm has wrong number of register infos"); + +// Number of register sets provided by this context. +enum +{ + k_num_register_sets = 2 +}; + +// Register sets for arm. +static const lldb_private::RegisterSet +g_reg_sets_arm[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_arm, g_gpr_regnums_arm }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_arm, g_fpu_regnums_arm } +}; + +bool RegisterContextPOSIX_arm::IsGPR(unsigned reg) +{ + return reg <= m_reg_info.last_gpr; // GPR's come first. +} + +bool RegisterContextPOSIX_arm::IsFPR(unsigned reg) +{ + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +RegisterContextPOSIX_arm::RegisterContextPOSIX_arm(lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info) + : lldb_private::RegisterContext(thread, concrete_frame_idx) +{ + m_register_info_ap.reset(register_info); + + switch (register_info->m_target_arch.GetMachine()) + { + case llvm::Triple::arm: + m_reg_info.num_registers = k_num_registers_arm; + m_reg_info.num_gpr_registers = k_num_gpr_registers_arm; + m_reg_info.num_fpr_registers = k_num_fpr_registers_arm; + m_reg_info.last_gpr = k_last_gpr_arm; + m_reg_info.first_fpr = k_first_fpr_arm; + m_reg_info.last_fpr = k_last_fpr_arm; + m_reg_info.first_fpr_v = fpu_s0_arm; + m_reg_info.last_fpr_v = fpu_s31_arm; + m_reg_info.gpr_flags = gpr_cpsr_arm; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } + + ::memset(&m_fpr, 0, sizeof m_fpr); + + // elf-core yet to support ReadFPR() + lldb::ProcessSP base = CalculateProcess(); + if (base.get()->GetPluginName() == ProcessElfCore::GetPluginNameStatic()) + return; +} + +RegisterContextPOSIX_arm::~RegisterContextPOSIX_arm() +{ +} + +void +RegisterContextPOSIX_arm::Invalidate() +{ +} + +void +RegisterContextPOSIX_arm::InvalidateAllRegisters() +{ +} + +unsigned +RegisterContextPOSIX_arm::GetRegisterOffset(unsigned reg) +{ + assert(reg < m_reg_info.num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned +RegisterContextPOSIX_arm::GetRegisterSize(unsigned reg) +{ + assert(reg < m_reg_info.num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t +RegisterContextPOSIX_arm::GetRegisterCount() +{ + size_t num_registers = m_reg_info.num_gpr_registers + m_reg_info.num_fpr_registers; + return num_registers; +} + +size_t +RegisterContextPOSIX_arm::GetGPRSize() +{ + return m_register_info_ap->GetGPRSize (); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_arm::GetRegisterInfo() +{ + // Commonly, this method is overridden and g_register_infos is copied and specialized. + // So, use GetRegisterInfo() rather than g_register_infos in this scope. + return m_register_info_ap->GetRegisterInfo (); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_arm::GetRegisterInfoAtIndex(size_t reg) +{ + if (reg < m_reg_info.num_registers) + return &GetRegisterInfo()[reg]; + else + return NULL; +} + +size_t +RegisterContextPOSIX_arm::GetRegisterSetCount() +{ + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) + { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const lldb_private::RegisterSet * +RegisterContextPOSIX_arm::GetRegisterSet(size_t set) +{ + if (IsRegisterSetAvailable(set)) + { + switch (m_register_info_ap->m_target_arch.GetMachine()) + { + case llvm::Triple::arm: + return &g_reg_sets_arm[set]; + default: + assert(false && "Unhandled target architecture."); + return NULL; + } + } + return NULL; +} + +const char * +RegisterContextPOSIX_arm::GetRegisterName(unsigned reg) +{ + assert(reg < m_reg_info.num_registers && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +lldb::ByteOrder +RegisterContextPOSIX_arm::GetByteOrder() +{ + // Get the target process whose privileged thread was used for the register read. + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid; + lldb_private::Process *process = CalculateProcess().get(); + + if (process) + byte_order = process->GetByteOrder(); + return byte_order; +} + +bool +RegisterContextPOSIX_arm::IsRegisterSetAvailable(size_t set_index) +{ + return set_index < k_num_register_sets; +} + + +// Used when parsing DWARF and EH frame information and any other +// object file sections that contain register numbers in them. +uint32_t +RegisterContextPOSIX_arm::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert (kind < lldb::kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const lldb_private::RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h new file mode 100644 index 000000000000..a3a2926262f7 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h @@ -0,0 +1,121 @@ +//===-- RegisterContextPOSIX_arm.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextPOSIX_arm_H_ +#define liblldb_RegisterContextPOSIX_arm_H_ + +#include "lldb/Core/Log.h" +#include "lldb-arm-register-enums.h" +#include "RegisterContextPOSIX.h" + +class ProcessMonitor; + +class RegisterContextPOSIX_arm + : public lldb_private::RegisterContext +{ +public: + RegisterContextPOSIX_arm (lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_arm(); + + void + Invalidate(); + + void + InvalidateAllRegisters(); + + size_t + GetRegisterCount(); + + virtual size_t + GetGPRSize(); + + virtual unsigned + GetRegisterSize(unsigned reg); + + virtual unsigned + GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(size_t reg); + + size_t + GetRegisterSetCount(); + + const lldb_private::RegisterSet * + GetRegisterSet(size_t set); + + const char * + GetRegisterName(unsigned reg); + + uint32_t + ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num); + +protected: + struct RegInfo + { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_fpr_v; + uint32_t last_fpr_v; + + uint32_t gpr_flags; + }; + + struct QReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + + uint32_t m_gpr_arm[lldb_private::k_num_gpr_registers_arm]; // 32-bit general purpose registers. + RegInfo m_reg_info; + struct RegisterContextPOSIX_arm::FPU m_fpr; // floating-point registers including extended register sets. + std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_ap; // Register Info Interface (FreeBSD or Linux) + + // Determines if an extended register set is supported on the processor running the inferior process. + virtual bool + IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfo(); + + bool + IsGPR(unsigned reg); + + bool + IsFPR(unsigned reg); + + lldb::ByteOrder GetByteOrder(); + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // #ifndef liblldb_RegisterContextPOSIX_arm_H_ + diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp index ea54a9ad9cf4..96508eafcce2 100644 --- a/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp @@ -23,6 +23,9 @@ #include "RegisterContextPOSIX_arm64.h" #include "Plugins/Process/elf-core/ProcessElfCore.h" +using namespace lldb; +using namespace lldb_private; + // ARM64 general purpose registers. const uint32_t g_gpr_regnums_arm64[] = { diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h index 3639960ef3de..29e7a7d21e02 100644 --- a/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h @@ -11,164 +11,11 @@ #define liblldb_RegisterContextPOSIX_arm64_H_ #include "lldb/Core/Log.h" +#include "lldb-arm64-register-enums.h" #include "RegisterContextPOSIX.h" class ProcessMonitor; -//--------------------------------------------------------------------------- -// Internal codes for all ARM64 registers. -//--------------------------------------------------------------------------- -enum -{ - k_first_gpr_arm64, - gpr_x0_arm64 = k_first_gpr_arm64, - gpr_x1_arm64, - gpr_x2_arm64, - gpr_x3_arm64, - gpr_x4_arm64, - gpr_x5_arm64, - gpr_x6_arm64, - gpr_x7_arm64, - gpr_x8_arm64, - gpr_x9_arm64, - gpr_x10_arm64, - gpr_x11_arm64, - gpr_x12_arm64, - gpr_x13_arm64, - gpr_x14_arm64, - gpr_x15_arm64, - gpr_x16_arm64, - gpr_x17_arm64, - gpr_x18_arm64, - gpr_x19_arm64, - gpr_x20_arm64, - gpr_x21_arm64, - gpr_x22_arm64, - gpr_x23_arm64, - gpr_x24_arm64, - gpr_x25_arm64, - gpr_x26_arm64, - gpr_x27_arm64, - gpr_x28_arm64, - gpr_fp_arm64, - gpr_lr_arm64, - gpr_sp_arm64, - gpr_pc_arm64, - gpr_cpsr_arm64, - - k_last_gpr_arm64 = gpr_cpsr_arm64, - - k_first_fpr_arm64, - fpu_v0_arm64 = k_first_fpr_arm64, - fpu_v1_arm64, - fpu_v2_arm64, - fpu_v3_arm64, - fpu_v4_arm64, - fpu_v5_arm64, - fpu_v6_arm64, - fpu_v7_arm64, - fpu_v8_arm64, - fpu_v9_arm64, - fpu_v10_arm64, - fpu_v11_arm64, - fpu_v12_arm64, - fpu_v13_arm64, - fpu_v14_arm64, - fpu_v15_arm64, - fpu_v16_arm64, - fpu_v17_arm64, - fpu_v18_arm64, - fpu_v19_arm64, - fpu_v20_arm64, - fpu_v21_arm64, - fpu_v22_arm64, - fpu_v23_arm64, - fpu_v24_arm64, - fpu_v25_arm64, - fpu_v26_arm64, - fpu_v27_arm64, - fpu_v28_arm64, - fpu_v29_arm64, - fpu_v30_arm64, - fpu_v31_arm64, - fpu_fpsr_arm64, - fpu_fpcr_arm64, - k_last_fpr_arm64 = fpu_fpcr_arm64, - - exc_far_arm64, - exc_esr_arm64, - exc_exception_arm64, - - dbg_bvr0_arm64, - dbg_bvr1_arm64, - dbg_bvr2_arm64, - dbg_bvr3_arm64, - dbg_bvr4_arm64, - dbg_bvr5_arm64, - dbg_bvr6_arm64, - dbg_bvr7_arm64, - dbg_bvr8_arm64, - dbg_bvr9_arm64, - dbg_bvr10_arm64, - dbg_bvr11_arm64, - dbg_bvr12_arm64, - dbg_bvr13_arm64, - dbg_bvr14_arm64, - dbg_bvr15_arm64, - dbg_bcr0_arm64, - dbg_bcr1_arm64, - dbg_bcr2_arm64, - dbg_bcr3_arm64, - dbg_bcr4_arm64, - dbg_bcr5_arm64, - dbg_bcr6_arm64, - dbg_bcr7_arm64, - dbg_bcr8_arm64, - dbg_bcr9_arm64, - dbg_bcr10_arm64, - dbg_bcr11_arm64, - dbg_bcr12_arm64, - dbg_bcr13_arm64, - dbg_bcr14_arm64, - dbg_bcr15_arm64, - dbg_wvr0_arm64, - dbg_wvr1_arm64, - dbg_wvr2_arm64, - dbg_wvr3_arm64, - dbg_wvr4_arm64, - dbg_wvr5_arm64, - dbg_wvr6_arm64, - dbg_wvr7_arm64, - dbg_wvr8_arm64, - dbg_wvr9_arm64, - dbg_wvr10_arm64, - dbg_wvr11_arm64, - dbg_wvr12_arm64, - dbg_wvr13_arm64, - dbg_wvr14_arm64, - dbg_wvr15_arm64, - dbg_wcr0_arm64, - dbg_wcr1_arm64, - dbg_wcr2_arm64, - dbg_wcr3_arm64, - dbg_wcr4_arm64, - dbg_wcr5_arm64, - dbg_wcr6_arm64, - dbg_wcr7_arm64, - dbg_wcr8_arm64, - dbg_wcr9_arm64, - dbg_wcr10_arm64, - dbg_wcr11_arm64, - dbg_wcr12_arm64, - dbg_wcr13_arm64, - dbg_wcr14_arm64, - dbg_wcr15_arm64, - - k_num_registers_arm64, - k_num_gpr_registers_arm64 = k_last_gpr_arm64 - k_first_gpr_arm64 + 1, - k_num_fpr_registers_arm64 = k_last_fpr_arm64 - k_first_fpr_arm64 + 1 -}; - class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext { @@ -243,9 +90,9 @@ protected: uint32_t fpcr; }; - uint64_t m_gpr_arm64[k_num_gpr_registers_arm64]; // 64-bit general purpose registers. + uint64_t m_gpr_arm64[lldb_private::k_num_gpr_registers_arm64]; // 64-bit general purpose registers. RegInfo m_reg_info; - struct RegisterContextPOSIX_arm64::FPU m_fpr; // floating-point registers including extended register sets. + struct RegisterContextPOSIX_arm64::FPU m_fpr; // floating-point registers including extended register sets. std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_ap; // Register Info Interface (FreeBSD or Linux) // Determines if an extended register set is supported on the processor running the inferior process. diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h b/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h index 991179bdec66..2a61685f12ac 100644 --- a/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h @@ -13,59 +13,11 @@ #include "lldb/Core/Log.h" #include "RegisterContextPOSIX.h" #include "RegisterContext_mips64.h" +#include "lldb-mips64-register-enums.h" -class ProcessMonitor; +using namespace lldb_private; -// --------------------------------------------------------------------------- -// Internal codes for all mips64 registers. -// --------------------------------------------------------------------------- -enum -{ - k_first_gpr_mips64, - gpr_zero_mips64 = k_first_gpr_mips64, - gpr_r1_mips64, - gpr_r2_mips64, - gpr_r3_mips64, - gpr_r4_mips64, - gpr_r5_mips64, - gpr_r6_mips64, - gpr_r7_mips64, - gpr_r8_mips64, - gpr_r9_mips64, - gpr_r10_mips64, - gpr_r11_mips64, - gpr_r12_mips64, - gpr_r13_mips64, - gpr_r14_mips64, - gpr_r15_mips64, - gpr_r16_mips64, - gpr_r17_mips64, - gpr_r18_mips64, - gpr_r19_mips64, - gpr_r20_mips64, - gpr_r21_mips64, - gpr_r22_mips64, - gpr_r23_mips64, - gpr_r24_mips64, - gpr_r25_mips64, - gpr_r26_mips64, - gpr_r27_mips64, - gpr_gp_mips64, - gpr_sp_mips64, - gpr_r30_mips64, - gpr_ra_mips64, - gpr_sr_mips64, - gpr_mullo_mips64, - gpr_mulhi_mips64, - gpr_badvaddr_mips64, - gpr_cause_mips64, - gpr_pc_mips64, - gpr_ic_mips64, - gpr_dummy_mips64, - - k_num_registers_mips64, - k_num_gpr_registers_mips64 = k_num_registers_mips64 -}; +class ProcessMonitor; class RegisterContextPOSIX_mips64 : public lldb_private::RegisterContext diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp b/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp index 828fb2571f79..e353e8114765 100644 --- a/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp @@ -159,9 +159,10 @@ g_reg_sets_powerpc[k_num_register_sets] = { "Altivec/VMX Registers", "vmx", k_num_vmx_registers_powerpc, g_vmx_regnums }, }; +static_assert(k_first_gpr_powerpc == 0, "GPRs must index starting at 0, or fix IsGPR()"); bool RegisterContextPOSIX_powerpc::IsGPR(unsigned reg) { - return (reg >= k_first_gpr_powerpc) && (reg <= k_last_gpr_powerpc); // GPR's come first. + return (reg <= k_last_gpr_powerpc); // GPR's come first. } bool diff --git a/source/Plugins/Process/Utility/RegisterContext_mips64.h b/source/Plugins/Process/Utility/RegisterContext_mips64.h index dfd473d7cbec..96ce2a0acc5b 100644 --- a/source/Plugins/Process/Utility/RegisterContext_mips64.h +++ b/source/Plugins/Process/Utility/RegisterContext_mips64.h @@ -14,6 +14,84 @@ enum { // GP Registers + gcc_dwarf_zero_mips = 0, + gcc_dwarf_r1_mips, + gcc_dwarf_r2_mips, + gcc_dwarf_r3_mips, + gcc_dwarf_r4_mips, + gcc_dwarf_r5_mips, + gcc_dwarf_r6_mips, + gcc_dwarf_r7_mips, + gcc_dwarf_r8_mips, + gcc_dwarf_r9_mips, + gcc_dwarf_r10_mips, + gcc_dwarf_r11_mips, + gcc_dwarf_r12_mips, + gcc_dwarf_r13_mips, + gcc_dwarf_r14_mips, + gcc_dwarf_r15_mips, + gcc_dwarf_r16_mips, + gcc_dwarf_r17_mips, + gcc_dwarf_r18_mips, + gcc_dwarf_r19_mips, + gcc_dwarf_r20_mips, + gcc_dwarf_r21_mips, + gcc_dwarf_r22_mips, + gcc_dwarf_r23_mips, + gcc_dwarf_r24_mips, + gcc_dwarf_r25_mips, + gcc_dwarf_r26_mips, + gcc_dwarf_r27_mips, + gcc_dwarf_gp_mips, + gcc_dwarf_sp_mips, + gcc_dwarf_r30_mips, + gcc_dwarf_ra_mips, + gcc_dwarf_lo_mips, + gcc_dwarf_hi_mips, + gcc_dwarf_pc_mips, + gcc_dwarf_bad_mips, + gcc_dwarf_sr_mips, + gcc_dwarf_cause_mips, + gcc_dwarf_f0_mips, + gcc_dwarf_f1_mips, + gcc_dwarf_f2_mips, + gcc_dwarf_f3_mips, + gcc_dwarf_f4_mips, + gcc_dwarf_f5_mips, + gcc_dwarf_f6_mips, + gcc_dwarf_f7_mips, + gcc_dwarf_f8_mips, + gcc_dwarf_f9_mips, + gcc_dwarf_f10_mips, + gcc_dwarf_f11_mips, + gcc_dwarf_f12_mips, + gcc_dwarf_f13_mips, + gcc_dwarf_f14_mips, + gcc_dwarf_f15_mips, + gcc_dwarf_f16_mips, + gcc_dwarf_f17_mips, + gcc_dwarf_f18_mips, + gcc_dwarf_f19_mips, + gcc_dwarf_f20_mips, + gcc_dwarf_f21_mips, + gcc_dwarf_f22_mips, + gcc_dwarf_f23_mips, + gcc_dwarf_f24_mips, + gcc_dwarf_f25_mips, + gcc_dwarf_f26_mips, + gcc_dwarf_f27_mips, + gcc_dwarf_f28_mips, + gcc_dwarf_f29_mips, + gcc_dwarf_f30_mips, + gcc_dwarf_f31_mips, + gcc_dwarf_fcsr_mips, + gcc_dwarf_fir_mips, + gcc_dwarf_ic_mips, + gcc_dwarf_dummy_mips +}; + +enum +{ gcc_dwarf_zero_mips64 = 0, gcc_dwarf_r1_mips64, gcc_dwarf_r2_mips64, @@ -52,6 +130,40 @@ enum gcc_dwarf_bad_mips64, gcc_dwarf_cause_mips64, gcc_dwarf_pc_mips64, + gcc_dwarf_f0_mips64, + gcc_dwarf_f1_mips64, + gcc_dwarf_f2_mips64, + gcc_dwarf_f3_mips64, + gcc_dwarf_f4_mips64, + gcc_dwarf_f5_mips64, + gcc_dwarf_f6_mips64, + gcc_dwarf_f7_mips64, + gcc_dwarf_f8_mips64, + gcc_dwarf_f9_mips64, + gcc_dwarf_f10_mips64, + gcc_dwarf_f11_mips64, + gcc_dwarf_f12_mips64, + gcc_dwarf_f13_mips64, + gcc_dwarf_f14_mips64, + gcc_dwarf_f15_mips64, + gcc_dwarf_f16_mips64, + gcc_dwarf_f17_mips64, + gcc_dwarf_f18_mips64, + gcc_dwarf_f19_mips64, + gcc_dwarf_f20_mips64, + gcc_dwarf_f21_mips64, + gcc_dwarf_f22_mips64, + gcc_dwarf_f23_mips64, + gcc_dwarf_f24_mips64, + gcc_dwarf_f25_mips64, + gcc_dwarf_f26_mips64, + gcc_dwarf_f27_mips64, + gcc_dwarf_f28_mips64, + gcc_dwarf_f29_mips64, + gcc_dwarf_f30_mips64, + gcc_dwarf_f31_mips64, + gcc_dwarf_fcsr_mips64, + gcc_dwarf_fir_mips64, gcc_dwarf_ic_mips64, gcc_dwarf_dummy_mips64 }; @@ -59,6 +171,84 @@ enum // GDB Register numbers (eRegisterKindGDB) enum { + gdb_zero_mips = 0, + gdb_r1_mips, + gdb_r2_mips, + gdb_r3_mips, + gdb_r4_mips, + gdb_r5_mips, + gdb_r6_mips, + gdb_r7_mips, + gdb_r8_mips, + gdb_r9_mips, + gdb_r10_mips, + gdb_r11_mips, + gdb_r12_mips, + gdb_r13_mips, + gdb_r14_mips, + gdb_r15_mips, + gdb_r16_mips, + gdb_r17_mips, + gdb_r18_mips, + gdb_r19_mips, + gdb_r20_mips, + gdb_r21_mips, + gdb_r22_mips, + gdb_r23_mips, + gdb_r24_mips, + gdb_r25_mips, + gdb_r26_mips, + gdb_r27_mips, + gdb_gp_mips, + gdb_sp_mips, + gdb_r30_mips, + gdb_ra_mips, + gdb_lo_mips, + gdb_hi_mips, + gdb_pc_mips, + gdb_bad_mips, + gdb_sr_mips, + gdb_cause_mips, + gdb_f0_mips, + gdb_f1_mips, + gdb_f2_mips, + gdb_f3_mips, + gdb_f4_mips, + gdb_f5_mips, + gdb_f6_mips, + gdb_f7_mips, + gdb_f8_mips, + gdb_f9_mips, + gdb_f10_mips, + gdb_f11_mips, + gdb_f12_mips, + gdb_f13_mips, + gdb_f14_mips, + gdb_f15_mips, + gdb_f16_mips, + gdb_f17_mips, + gdb_f18_mips, + gdb_f19_mips, + gdb_f20_mips, + gdb_f21_mips, + gdb_f22_mips, + gdb_f23_mips, + gdb_f24_mips, + gdb_f25_mips, + gdb_f26_mips, + gdb_f27_mips, + gdb_f28_mips, + gdb_f29_mips, + gdb_f30_mips, + gdb_f31_mips, + gdb_fcsr_mips, + gdb_fir_mips, + gdb_ic_mips, + gdb_dummy_mips +}; + +enum +{ gdb_zero_mips64 = 0, gdb_r1_mips64, gdb_r2_mips64, @@ -97,8 +287,50 @@ enum gdb_bad_mips64, gdb_cause_mips64, gdb_pc_mips64, + gdb_f0_mips64, + gdb_f1_mips64, + gdb_f2_mips64, + gdb_f3_mips64, + gdb_f4_mips64, + gdb_f5_mips64, + gdb_f6_mips64, + gdb_f7_mips64, + gdb_f8_mips64, + gdb_f9_mips64, + gdb_f10_mips64, + gdb_f11_mips64, + gdb_f12_mips64, + gdb_f13_mips64, + gdb_f14_mips64, + gdb_f15_mips64, + gdb_f16_mips64, + gdb_f17_mips64, + gdb_f18_mips64, + gdb_f19_mips64, + gdb_f20_mips64, + gdb_f21_mips64, + gdb_f22_mips64, + gdb_f23_mips64, + gdb_f24_mips64, + gdb_f25_mips64, + gdb_f26_mips64, + gdb_f27_mips64, + gdb_f28_mips64, + gdb_f29_mips64, + gdb_f30_mips64, + gdb_f31_mips64, + gdb_fcsr_mips64, + gdb_fir_mips64, gdb_ic_mips64, gdb_dummy_mips64 }; +// FP registers +struct FPR_mips +{ + uint64_t fp_reg[32]; + uint32_t fcsr; /* FPU status register */ + uint32_t fir; /* FPU control register */ +}; + #endif // liblldb_RegisterContext_mips64_H_ diff --git a/source/Plugins/Process/Utility/RegisterInfos_arm.h b/source/Plugins/Process/Utility/RegisterInfos_arm.h new file mode 100644 index 000000000000..3d144d669415 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterInfos_arm.h @@ -0,0 +1,303 @@ +//===-- RegisterInfos_arm.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_ARM_STRUCT + +#include <stddef.h> + +#include "lldb/lldb-private.h" +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" + +#include "Utility/ARM_GCC_Registers.h" +#include "Utility/ARM_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef GPR_OFFSET +#error GPR_OFFSET must be defined before including this header file +#endif + + +#ifndef FPU_OFFSET +#error FPU_OFFSET must be defined before including this header file +#endif + +#ifndef EXC_OFFSET +#error EXC_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef DBG_OFFSET +#error DBG_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef DEFINE_DBG +#error DEFINE_DBG must be defined before including this header file +#endif + +enum +{ + gpr_r0 = 0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, gpr_sp = gpr_r13, + gpr_r14, gpr_lr = gpr_r14, + gpr_r15, gpr_pc = gpr_r15, + gpr_cpsr, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, + + exc_exception, + exc_fsr, + exc_far, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + +static RegisterInfo g_register_infos_arm[] = { +// General purpose registers +// NAME ALT SZ OFFSET ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS +// ====== ======= == ============= ============= ============ =============== =============== ========================= ===================== ============= ========== =============== +{ "r0", NULL, 4, GPR_OFFSET(0), eEncodingUint, eFormatHex, { gcc_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, gdb_arm_r0, gpr_r0 }, NULL, NULL}, +{ "r1", NULL, 4, GPR_OFFSET(1), eEncodingUint, eFormatHex, { gcc_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, gdb_arm_r1, gpr_r1 }, NULL, NULL}, +{ "r2", NULL, 4, GPR_OFFSET(2), eEncodingUint, eFormatHex, { gcc_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, gdb_arm_r2, gpr_r2 }, NULL, NULL}, +{ "r3", NULL, 4, GPR_OFFSET(3), eEncodingUint, eFormatHex, { gcc_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, gdb_arm_r3, gpr_r3 }, NULL, NULL}, +{ "r4", NULL, 4, GPR_OFFSET(4), eEncodingUint, eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, gdb_arm_r4, gpr_r4 }, NULL, NULL}, +{ "r5", NULL, 4, GPR_OFFSET(5), eEncodingUint, eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, gdb_arm_r5, gpr_r5 }, NULL, NULL}, +{ "r6", NULL, 4, GPR_OFFSET(6), eEncodingUint, eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, gdb_arm_r6, gpr_r6 }, NULL, NULL}, +{ "r7", NULL, 4, GPR_OFFSET(7), eEncodingUint, eFormatHex, { gcc_r7, dwarf_r7, LLDB_INVALID_REGNUM, gdb_arm_r7, gpr_r7 }, NULL, NULL}, +{ "r8", NULL, 4, GPR_OFFSET(8), eEncodingUint, eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, gdb_arm_r8, gpr_r8 }, NULL, NULL}, +{ "r9", NULL, 4, GPR_OFFSET(9), eEncodingUint, eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, gdb_arm_r9, gpr_r9 }, NULL, NULL}, +{ "r10", NULL, 4, GPR_OFFSET(10), eEncodingUint, eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, gdb_arm_r10, gpr_r10 }, NULL, NULL}, +{ "r11", NULL, 4, GPR_OFFSET(11), eEncodingUint, eFormatHex, { gcc_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, gdb_arm_r11, gpr_r11 }, NULL, NULL}, +{ "r12", NULL, 4, GPR_OFFSET(12), eEncodingUint, eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, gdb_arm_r12, gpr_r12 }, NULL, NULL}, +{ "sp", "r13", 4, GPR_OFFSET(13), eEncodingUint, eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, gdb_arm_sp, gpr_sp }, NULL, NULL}, +{ "lr", "r14", 4, GPR_OFFSET(14), eEncodingUint, eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_arm_lr, gpr_lr }, NULL, NULL}, +{ "pc", "r15", 4, GPR_OFFSET(15), eEncodingUint, eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, gdb_arm_pc, gpr_pc }, NULL, NULL}, +{ "cpsr", "psr", 4, GPR_OFFSET(16), eEncodingUint, eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, gdb_arm_cpsr, gpr_cpsr }, NULL, NULL}, + +{ "s0", NULL, 4, FPU_OFFSET(0), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, gdb_arm_s0, fpu_s0 }, NULL, NULL}, +{ "s1", NULL, 4, FPU_OFFSET(1), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, gdb_arm_s1, fpu_s1 }, NULL, NULL}, +{ "s2", NULL, 4, FPU_OFFSET(2), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, gdb_arm_s2, fpu_s2 }, NULL, NULL}, +{ "s3", NULL, 4, FPU_OFFSET(3), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, gdb_arm_s3, fpu_s3 }, NULL, NULL}, +{ "s4", NULL, 4, FPU_OFFSET(4), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, gdb_arm_s4, fpu_s4 }, NULL, NULL}, +{ "s5", NULL, 4, FPU_OFFSET(5), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, gdb_arm_s5, fpu_s5 }, NULL, NULL}, +{ "s6", NULL, 4, FPU_OFFSET(6), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, gdb_arm_s6, fpu_s6 }, NULL, NULL}, +{ "s7", NULL, 4, FPU_OFFSET(7), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, gdb_arm_s7, fpu_s7 }, NULL, NULL}, +{ "s8", NULL, 4, FPU_OFFSET(8), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, gdb_arm_s8, fpu_s8 }, NULL, NULL}, +{ "s9", NULL, 4, FPU_OFFSET(9), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, gdb_arm_s9, fpu_s9 }, NULL, NULL}, +{ "s10", NULL, 4, FPU_OFFSET(10), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, gdb_arm_s10, fpu_s10 }, NULL, NULL}, +{ "s11", NULL, 4, FPU_OFFSET(11), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, gdb_arm_s11, fpu_s11 }, NULL, NULL}, +{ "s12", NULL, 4, FPU_OFFSET(12), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, gdb_arm_s12, fpu_s12 }, NULL, NULL}, +{ "s13", NULL, 4, FPU_OFFSET(13), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, gdb_arm_s13, fpu_s13 }, NULL, NULL}, +{ "s14", NULL, 4, FPU_OFFSET(14), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, gdb_arm_s14, fpu_s14 }, NULL, NULL}, +{ "s15", NULL, 4, FPU_OFFSET(15), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, gdb_arm_s15, fpu_s15 }, NULL, NULL}, +{ "s16", NULL, 4, FPU_OFFSET(16), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, gdb_arm_s16, fpu_s16 }, NULL, NULL}, +{ "s17", NULL, 4, FPU_OFFSET(17), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, gdb_arm_s17, fpu_s17 }, NULL, NULL}, +{ "s18", NULL, 4, FPU_OFFSET(18), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, gdb_arm_s18, fpu_s18 }, NULL, NULL}, +{ "s19", NULL, 4, FPU_OFFSET(19), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, gdb_arm_s19, fpu_s19 }, NULL, NULL}, +{ "s20", NULL, 4, FPU_OFFSET(20), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, gdb_arm_s20, fpu_s20 }, NULL, NULL}, +{ "s21", NULL, 4, FPU_OFFSET(21), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, gdb_arm_s21, fpu_s21 }, NULL, NULL}, +{ "s22", NULL, 4, FPU_OFFSET(22), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, gdb_arm_s22, fpu_s22 }, NULL, NULL}, +{ "s23", NULL, 4, FPU_OFFSET(23), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, gdb_arm_s23, fpu_s23 }, NULL, NULL}, +{ "s24", NULL, 4, FPU_OFFSET(24), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, gdb_arm_s24, fpu_s24 }, NULL, NULL}, +{ "s25", NULL, 4, FPU_OFFSET(25), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, gdb_arm_s25, fpu_s25 }, NULL, NULL}, +{ "s26", NULL, 4, FPU_OFFSET(26), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, gdb_arm_s26, fpu_s26 }, NULL, NULL}, +{ "s27", NULL, 4, FPU_OFFSET(27), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, gdb_arm_s27, fpu_s27 }, NULL, NULL}, +{ "s28", NULL, 4, FPU_OFFSET(28), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, gdb_arm_s28, fpu_s28 }, NULL, NULL}, +{ "s29", NULL, 4, FPU_OFFSET(29), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, gdb_arm_s29, fpu_s29 }, NULL, NULL}, +{ "s30", NULL, 4, FPU_OFFSET(30), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, gdb_arm_s30, fpu_s30 }, NULL, NULL}, +{ "s31", NULL, 4, FPU_OFFSET(31), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, gdb_arm_s31, fpu_s31 }, NULL, NULL}, +{ "fpscr", NULL, 4, FPU_OFFSET(32), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, gdb_arm_fpscr, fpu_fpscr }, NULL, NULL}, + +{ "exception",NULL, 4, EXC_OFFSET(0), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_exception }, NULL, NULL}, +{ "fsr", NULL, 4, EXC_OFFSET(1), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_fsr }, NULL, NULL}, +{ "far", NULL, 4, EXC_OFFSET(2), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_far }, NULL, NULL}, + +{ DEFINE_DBG (bvr, 0) }, +{ DEFINE_DBG (bvr, 1) }, +{ DEFINE_DBG (bvr, 2) }, +{ DEFINE_DBG (bvr, 3) }, +{ DEFINE_DBG (bvr, 4) }, +{ DEFINE_DBG (bvr, 5) }, +{ DEFINE_DBG (bvr, 6) }, +{ DEFINE_DBG (bvr, 7) }, +{ DEFINE_DBG (bvr, 8) }, +{ DEFINE_DBG (bvr, 9) }, +{ DEFINE_DBG (bvr, 10) }, +{ DEFINE_DBG (bvr, 11) }, +{ DEFINE_DBG (bvr, 12) }, +{ DEFINE_DBG (bvr, 13) }, +{ DEFINE_DBG (bvr, 14) }, +{ DEFINE_DBG (bvr, 15) }, + +{ DEFINE_DBG (bcr, 0) }, +{ DEFINE_DBG (bcr, 1) }, +{ DEFINE_DBG (bcr, 2) }, +{ DEFINE_DBG (bcr, 3) }, +{ DEFINE_DBG (bcr, 4) }, +{ DEFINE_DBG (bcr, 5) }, +{ DEFINE_DBG (bcr, 6) }, +{ DEFINE_DBG (bcr, 7) }, +{ DEFINE_DBG (bcr, 8) }, +{ DEFINE_DBG (bcr, 9) }, +{ DEFINE_DBG (bcr, 10) }, +{ DEFINE_DBG (bcr, 11) }, +{ DEFINE_DBG (bcr, 12) }, +{ DEFINE_DBG (bcr, 13) }, +{ DEFINE_DBG (bcr, 14) }, +{ DEFINE_DBG (bcr, 15) }, + +{ DEFINE_DBG (wvr, 0) }, +{ DEFINE_DBG (wvr, 1) }, +{ DEFINE_DBG (wvr, 2) }, +{ DEFINE_DBG (wvr, 3) }, +{ DEFINE_DBG (wvr, 4) }, +{ DEFINE_DBG (wvr, 5) }, +{ DEFINE_DBG (wvr, 6) }, +{ DEFINE_DBG (wvr, 7) }, +{ DEFINE_DBG (wvr, 8) }, +{ DEFINE_DBG (wvr, 9) }, +{ DEFINE_DBG (wvr, 10) }, +{ DEFINE_DBG (wvr, 11) }, +{ DEFINE_DBG (wvr, 12) }, +{ DEFINE_DBG (wvr, 13) }, +{ DEFINE_DBG (wvr, 14) }, +{ DEFINE_DBG (wvr, 15) }, + +{ DEFINE_DBG (wcr, 0) }, +{ DEFINE_DBG (wcr, 1) }, +{ DEFINE_DBG (wcr, 2) }, +{ DEFINE_DBG (wcr, 3) }, +{ DEFINE_DBG (wcr, 4) }, +{ DEFINE_DBG (wcr, 5) }, +{ DEFINE_DBG (wcr, 6) }, +{ DEFINE_DBG (wcr, 7) }, +{ DEFINE_DBG (wcr, 8) }, +{ DEFINE_DBG (wcr, 9) }, +{ DEFINE_DBG (wcr, 10) }, +{ DEFINE_DBG (wcr, 11) }, +{ DEFINE_DBG (wcr, 12) }, +{ DEFINE_DBG (wcr, 13) }, +{ DEFINE_DBG (wcr, 14) }, +{ DEFINE_DBG (wcr, 15) } +}; + +#endif // DECLARE_REGISTER_INFOS_ARM_STRUCT diff --git a/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/source/Plugins/Process/Utility/RegisterInfos_arm64.h index b687423622a4..0255a3bc7d60 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_arm64.h +++ b/source/Plugins/Process/Utility/RegisterInfos_arm64.h @@ -198,14 +198,14 @@ static lldb_private::RegisterInfo g_register_infos_arm64[] = { // General purpose registers // NAME ALT SZ OFFSET ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS // ====== ======= == ============= ============= ============ =============== =============== ========================= ===================== ============= ========== =============== -{ "x0", NULL, 8, GPR_OFFSET(0), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x0, arm64_dwarf::x0, LLDB_INVALID_REGNUM, arm64_gcc::x0, gpr_x0 }, NULL, NULL}, -{ "x1", NULL, 8, GPR_OFFSET(1), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x1, arm64_dwarf::x1, LLDB_INVALID_REGNUM, arm64_gcc::x1, gpr_x1 }, NULL, NULL}, -{ "x2", NULL, 8, GPR_OFFSET(2), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x2, arm64_dwarf::x2, LLDB_INVALID_REGNUM, arm64_gcc::x2, gpr_x2 }, NULL, NULL}, -{ "x3", NULL, 8, GPR_OFFSET(3), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x3, arm64_dwarf::x3, LLDB_INVALID_REGNUM, arm64_gcc::x3, gpr_x3 }, NULL, NULL}, -{ "x4", NULL, 8, GPR_OFFSET(4), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x4, arm64_dwarf::x4, LLDB_INVALID_REGNUM, arm64_gcc::x4, gpr_x4 }, NULL, NULL}, -{ "x5", NULL, 8, GPR_OFFSET(5), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x5, arm64_dwarf::x5, LLDB_INVALID_REGNUM, arm64_gcc::x5, gpr_x5 }, NULL, NULL}, -{ "x6", NULL, 8, GPR_OFFSET(6), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x6, arm64_dwarf::x6, LLDB_INVALID_REGNUM, arm64_gcc::x6, gpr_x6 }, NULL, NULL}, -{ "x7", NULL, 8, GPR_OFFSET(7), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x7, arm64_dwarf::x7, LLDB_INVALID_REGNUM, arm64_gcc::x7, gpr_x7 }, NULL, NULL}, +{ "x0", NULL, 8, GPR_OFFSET(0), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x0, arm64_dwarf::x0, LLDB_REGNUM_GENERIC_ARG1, arm64_gcc::x0, gpr_x0 }, NULL, NULL}, +{ "x1", NULL, 8, GPR_OFFSET(1), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x1, arm64_dwarf::x1, LLDB_REGNUM_GENERIC_ARG2, arm64_gcc::x1, gpr_x1 }, NULL, NULL}, +{ "x2", NULL, 8, GPR_OFFSET(2), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x2, arm64_dwarf::x2, LLDB_REGNUM_GENERIC_ARG3, arm64_gcc::x2, gpr_x2 }, NULL, NULL}, +{ "x3", NULL, 8, GPR_OFFSET(3), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x3, arm64_dwarf::x3, LLDB_REGNUM_GENERIC_ARG4, arm64_gcc::x3, gpr_x3 }, NULL, NULL}, +{ "x4", NULL, 8, GPR_OFFSET(4), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x4, arm64_dwarf::x4, LLDB_REGNUM_GENERIC_ARG5, arm64_gcc::x4, gpr_x4 }, NULL, NULL}, +{ "x5", NULL, 8, GPR_OFFSET(5), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x5, arm64_dwarf::x5, LLDB_REGNUM_GENERIC_ARG6, arm64_gcc::x5, gpr_x5 }, NULL, NULL}, +{ "x6", NULL, 8, GPR_OFFSET(6), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x6, arm64_dwarf::x6, LLDB_REGNUM_GENERIC_ARG7, arm64_gcc::x6, gpr_x6 }, NULL, NULL}, +{ "x7", NULL, 8, GPR_OFFSET(7), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x7, arm64_dwarf::x7, LLDB_REGNUM_GENERIC_ARG8, arm64_gcc::x7, gpr_x7 }, NULL, NULL}, { "x8", NULL, 8, GPR_OFFSET(8), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x8, arm64_dwarf::x8, LLDB_INVALID_REGNUM, arm64_gcc::x8, gpr_x8 }, NULL, NULL}, { "x9", NULL, 8, GPR_OFFSET(9), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x9, arm64_dwarf::x9, LLDB_INVALID_REGNUM, arm64_gcc::x9, gpr_x9 }, NULL, NULL}, { "x10", NULL, 8, GPR_OFFSET(10), lldb::eEncodingUint, lldb::eFormatHex, { arm64_gcc::x10, arm64_dwarf::x10, LLDB_INVALID_REGNUM, arm64_gcc::x10, gpr_x10 }, NULL, NULL}, diff --git a/source/Plugins/Process/Utility/RegisterInfos_i386.h b/source/Plugins/Process/Utility/RegisterInfos_i386.h index fc94b8b2a738..69825362134b 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_i386.h +++ b/source/Plugins/Process/Utility/RegisterInfos_i386.h @@ -18,12 +18,16 @@ // Computes the offset of the given FPR in the extended data area. #define FPR_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(FPR, xstate) + \ - LLVM_EXTENSION offsetof(FXSAVE, regname)) + (LLVM_EXTENSION offsetof(UserArea, i387) + \ + LLVM_EXTENSION offsetof(FPR_i386, regname)) // Computes the offset of the YMM register assembled from register halves. -#define YMM_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(YMM, regname)) +// Based on DNBArchImplI386.cpp from debugserver +#define YMM_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, i387) + \ + LLVM_EXTENSION offsetof(FPR, xstate) + \ + LLVM_EXTENSION offsetof(FXSAVE, xmm[7]) + \ + sizeof(XMMReg) + (32 * reg_index)) // Number of bytes needed to represent a FPR. #if !defined(FPR_SIZE) @@ -70,7 +74,7 @@ // I believe the YMM registers use dwarf_xmm_%_i386 register numbers and then differentiate based on register size. #define DEFINE_YMM(reg, i) \ - { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(reg[i]), \ + { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(i), \ eEncodingVector, eFormatVectorOfUInt8, \ { LLDB_INVALID_REGNUM, dwarf_xmm##i##_i386, LLDB_INVALID_REGNUM, gdb_##reg##i##h_i386, lldb_##reg##i##_i386 }, \ NULL, NULL } diff --git a/source/Plugins/Process/Utility/RegisterInfos_mips.h b/source/Plugins/Process/Utility/RegisterInfos_mips.h new file mode 100644 index 000000000000..0956b2ca9eca --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterInfos_mips.h @@ -0,0 +1,122 @@ +//===-- RegisterInfos_mips.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +#include "llvm/Support/Compiler.h" + +#include <stddef.h> + +#ifdef DECLARE_REGISTER_INFOS_MIPS_STRUCT + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(GPR, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(FPR_mips, regname)) + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((GPR*)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips }, NULL, NULL } + +#define DEFINE_FPR(member, reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_mips*)NULL)->member) / 2, FPR_OFFSET(member), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } + +#define DEFINE_FPR_INFO(member, reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_mips*)NULL)->member), FPR_OFFSET(member), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } + +// RegisterKind: GCC, DWARF, Generic, GDB, LLDB + +static RegisterInfo +g_register_infos_mips[] = +{ + DEFINE_GPR (zero, "zero", gcc_dwarf_zero_mips, gcc_dwarf_zero_mips, LLDB_INVALID_REGNUM, gdb_zero_mips), + DEFINE_GPR (r1, "at", gcc_dwarf_r1_mips, gcc_dwarf_r1_mips, LLDB_INVALID_REGNUM, gdb_r1_mips), + DEFINE_GPR (r2, NULL, gcc_dwarf_r2_mips, gcc_dwarf_r2_mips, LLDB_INVALID_REGNUM, gdb_r2_mips), + DEFINE_GPR (r3, NULL, gcc_dwarf_r3_mips, gcc_dwarf_r3_mips, LLDB_INVALID_REGNUM, gdb_r3_mips), + DEFINE_GPR (r4, NULL, gcc_dwarf_r4_mips, gcc_dwarf_r4_mips, LLDB_REGNUM_GENERIC_ARG1, gdb_r4_mips), + DEFINE_GPR (r5, NULL, gcc_dwarf_r5_mips, gcc_dwarf_r5_mips, LLDB_REGNUM_GENERIC_ARG2, gdb_r5_mips), + DEFINE_GPR (r6, NULL, gcc_dwarf_r6_mips, gcc_dwarf_r6_mips, LLDB_REGNUM_GENERIC_ARG3, gdb_r6_mips), + DEFINE_GPR (r7, NULL, gcc_dwarf_r7_mips, gcc_dwarf_r7_mips, LLDB_REGNUM_GENERIC_ARG4, gdb_r7_mips), + DEFINE_GPR (r8, NULL, gcc_dwarf_r8_mips, gcc_dwarf_r8_mips, LLDB_INVALID_REGNUM, gdb_r8_mips), + DEFINE_GPR (r9, NULL, gcc_dwarf_r9_mips, gcc_dwarf_r9_mips, LLDB_INVALID_REGNUM, gdb_r9_mips), + DEFINE_GPR (r10, NULL, gcc_dwarf_r10_mips, gcc_dwarf_r10_mips, LLDB_INVALID_REGNUM, gdb_r10_mips), + DEFINE_GPR (r11, NULL, gcc_dwarf_r11_mips, gcc_dwarf_r11_mips, LLDB_INVALID_REGNUM, gdb_r11_mips), + DEFINE_GPR (r12, NULL, gcc_dwarf_r12_mips, gcc_dwarf_r12_mips, LLDB_INVALID_REGNUM, gdb_r12_mips), + DEFINE_GPR (r13, NULL, gcc_dwarf_r13_mips, gcc_dwarf_r13_mips, LLDB_INVALID_REGNUM, gdb_r13_mips), + DEFINE_GPR (r14, NULL, gcc_dwarf_r14_mips, gcc_dwarf_r14_mips, LLDB_INVALID_REGNUM, gdb_r14_mips), + DEFINE_GPR (r15, NULL, gcc_dwarf_r15_mips, gcc_dwarf_r15_mips, LLDB_INVALID_REGNUM, gdb_r15_mips), + DEFINE_GPR (r16, NULL, gcc_dwarf_r16_mips, gcc_dwarf_r16_mips, LLDB_INVALID_REGNUM, gdb_r16_mips), + DEFINE_GPR (r17, NULL, gcc_dwarf_r17_mips, gcc_dwarf_r17_mips, LLDB_INVALID_REGNUM, gdb_r17_mips), + DEFINE_GPR (r18, NULL, gcc_dwarf_r18_mips, gcc_dwarf_r18_mips, LLDB_INVALID_REGNUM, gdb_r18_mips), + DEFINE_GPR (r19, NULL, gcc_dwarf_r19_mips, gcc_dwarf_r19_mips, LLDB_INVALID_REGNUM, gdb_r19_mips), + DEFINE_GPR (r20, NULL, gcc_dwarf_r20_mips, gcc_dwarf_r20_mips, LLDB_INVALID_REGNUM, gdb_r20_mips), + DEFINE_GPR (r21, NULL, gcc_dwarf_r21_mips, gcc_dwarf_r21_mips, LLDB_INVALID_REGNUM, gdb_r21_mips), + DEFINE_GPR (r22, NULL, gcc_dwarf_r22_mips, gcc_dwarf_r22_mips, LLDB_INVALID_REGNUM, gdb_r22_mips), + DEFINE_GPR (r23, NULL, gcc_dwarf_r23_mips, gcc_dwarf_r23_mips, LLDB_INVALID_REGNUM, gdb_r23_mips), + DEFINE_GPR (r24, NULL, gcc_dwarf_r24_mips, gcc_dwarf_r24_mips, LLDB_INVALID_REGNUM, gdb_r24_mips), + DEFINE_GPR (r25, NULL, gcc_dwarf_r25_mips, gcc_dwarf_r25_mips, LLDB_INVALID_REGNUM, gdb_r25_mips), + DEFINE_GPR (r26, NULL, gcc_dwarf_r26_mips, gcc_dwarf_r26_mips, LLDB_INVALID_REGNUM, gdb_r26_mips), + DEFINE_GPR (r27, NULL, gcc_dwarf_r27_mips, gcc_dwarf_r27_mips, LLDB_INVALID_REGNUM, gdb_r27_mips), + DEFINE_GPR (gp, "gp", gcc_dwarf_gp_mips, gcc_dwarf_gp_mips, LLDB_INVALID_REGNUM, gdb_gp_mips), + DEFINE_GPR (sp, "sp", gcc_dwarf_sp_mips, gcc_dwarf_sp_mips, LLDB_REGNUM_GENERIC_SP, gdb_sp_mips), + DEFINE_GPR (r30, "fp", gcc_dwarf_r30_mips, gcc_dwarf_r30_mips, LLDB_REGNUM_GENERIC_FP, gdb_r30_mips), + DEFINE_GPR (ra, "ra", gcc_dwarf_ra_mips, gcc_dwarf_ra_mips, LLDB_REGNUM_GENERIC_RA, gdb_ra_mips), + DEFINE_GPR (mullo, NULL, gcc_dwarf_lo_mips, gcc_dwarf_lo_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR (mulhi, NULL, gcc_dwarf_hi_mips, gcc_dwarf_hi_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR (pc, NULL, gcc_dwarf_pc_mips, gcc_dwarf_pc_mips, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR (badvaddr, NULL, gcc_dwarf_bad_mips, gcc_dwarf_bad_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR (sr, "status", gcc_dwarf_sr_mips, gcc_dwarf_sr_mips, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR (cause, NULL, gcc_dwarf_cause_mips, gcc_dwarf_cause_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR (fp_reg[0], f0, NULL, gcc_dwarf_f0_mips, gcc_dwarf_f0_mips, LLDB_INVALID_REGNUM, gdb_f0_mips), + DEFINE_FPR (fp_reg[1], f1, NULL, gcc_dwarf_f1_mips, gcc_dwarf_f1_mips, LLDB_INVALID_REGNUM, gdb_f1_mips), + DEFINE_FPR (fp_reg[2], f2, NULL, gcc_dwarf_f2_mips, gcc_dwarf_f2_mips, LLDB_INVALID_REGNUM, gdb_f2_mips), + DEFINE_FPR (fp_reg[3], f3, NULL, gcc_dwarf_f3_mips, gcc_dwarf_f3_mips, LLDB_INVALID_REGNUM, gdb_f3_mips), + DEFINE_FPR (fp_reg[4], f4, NULL, gcc_dwarf_f4_mips, gcc_dwarf_f4_mips, LLDB_INVALID_REGNUM, gdb_f4_mips), + DEFINE_FPR (fp_reg[5], f5, NULL, gcc_dwarf_f5_mips, gcc_dwarf_f5_mips, LLDB_INVALID_REGNUM, gdb_f5_mips), + DEFINE_FPR (fp_reg[6], f6, NULL, gcc_dwarf_f6_mips, gcc_dwarf_f6_mips, LLDB_INVALID_REGNUM, gdb_f6_mips), + DEFINE_FPR (fp_reg[7], f7, NULL, gcc_dwarf_f7_mips, gcc_dwarf_f7_mips, LLDB_INVALID_REGNUM, gdb_f7_mips), + DEFINE_FPR (fp_reg[8], f8, NULL, gcc_dwarf_f8_mips, gcc_dwarf_f8_mips, LLDB_INVALID_REGNUM, gdb_f8_mips), + DEFINE_FPR (fp_reg[9], f9, NULL, gcc_dwarf_f9_mips, gcc_dwarf_f9_mips, LLDB_INVALID_REGNUM, gdb_f9_mips), + DEFINE_FPR (fp_reg[10], f10, NULL, gcc_dwarf_f10_mips, gcc_dwarf_f10_mips, LLDB_INVALID_REGNUM, gdb_f10_mips), + DEFINE_FPR (fp_reg[11], f11, NULL, gcc_dwarf_f11_mips, gcc_dwarf_f11_mips, LLDB_INVALID_REGNUM, gdb_f11_mips), + DEFINE_FPR (fp_reg[12], f12, NULL, gcc_dwarf_f12_mips, gcc_dwarf_f12_mips, LLDB_INVALID_REGNUM, gdb_f12_mips), + DEFINE_FPR (fp_reg[13], f13, NULL, gcc_dwarf_f13_mips, gcc_dwarf_f13_mips, LLDB_INVALID_REGNUM, gdb_f13_mips), + DEFINE_FPR (fp_reg[14], f14, NULL, gcc_dwarf_f14_mips, gcc_dwarf_f14_mips, LLDB_INVALID_REGNUM, gdb_f14_mips), + DEFINE_FPR (fp_reg[15], f15, NULL, gcc_dwarf_f15_mips, gcc_dwarf_f15_mips, LLDB_INVALID_REGNUM, gdb_f15_mips), + DEFINE_FPR (fp_reg[16], f16, NULL, gcc_dwarf_f16_mips, gcc_dwarf_f16_mips, LLDB_INVALID_REGNUM, gdb_f16_mips), + DEFINE_FPR (fp_reg[17], f17, NULL, gcc_dwarf_f17_mips, gcc_dwarf_f17_mips, LLDB_INVALID_REGNUM, gdb_f17_mips), + DEFINE_FPR (fp_reg[18], f18, NULL, gcc_dwarf_f18_mips, gcc_dwarf_f18_mips, LLDB_INVALID_REGNUM, gdb_f18_mips), + DEFINE_FPR (fp_reg[19], f19, NULL, gcc_dwarf_f19_mips, gcc_dwarf_f19_mips, LLDB_INVALID_REGNUM, gdb_f19_mips), + DEFINE_FPR (fp_reg[20], f20, NULL, gcc_dwarf_f20_mips, gcc_dwarf_f20_mips, LLDB_INVALID_REGNUM, gdb_f20_mips), + DEFINE_FPR (fp_reg[21], f21, NULL, gcc_dwarf_f21_mips, gcc_dwarf_f21_mips, LLDB_INVALID_REGNUM, gdb_f21_mips), + DEFINE_FPR (fp_reg[22], f22, NULL, gcc_dwarf_f22_mips, gcc_dwarf_f22_mips, LLDB_INVALID_REGNUM, gdb_f22_mips), + DEFINE_FPR (fp_reg[23], f23, NULL, gcc_dwarf_f23_mips, gcc_dwarf_f23_mips, LLDB_INVALID_REGNUM, gdb_f23_mips), + DEFINE_FPR (fp_reg[24], f24, NULL, gcc_dwarf_f24_mips, gcc_dwarf_f24_mips, LLDB_INVALID_REGNUM, gdb_f24_mips), + DEFINE_FPR (fp_reg[25], f25, NULL, gcc_dwarf_f25_mips, gcc_dwarf_f25_mips, LLDB_INVALID_REGNUM, gdb_f25_mips), + DEFINE_FPR (fp_reg[26], f26, NULL, gcc_dwarf_f26_mips, gcc_dwarf_f26_mips, LLDB_INVALID_REGNUM, gdb_f26_mips), + DEFINE_FPR (fp_reg[27], f27, NULL, gcc_dwarf_f27_mips, gcc_dwarf_f27_mips, LLDB_INVALID_REGNUM, gdb_f27_mips), + DEFINE_FPR (fp_reg[28], f28, NULL, gcc_dwarf_f28_mips, gcc_dwarf_f28_mips, LLDB_INVALID_REGNUM, gdb_f28_mips), + DEFINE_FPR (fp_reg[29], f29, NULL, gcc_dwarf_f29_mips, gcc_dwarf_f29_mips, LLDB_INVALID_REGNUM, gdb_f29_mips), + DEFINE_FPR (fp_reg[30], f30, NULL, gcc_dwarf_f30_mips, gcc_dwarf_f30_mips, LLDB_INVALID_REGNUM, gdb_f30_mips), + DEFINE_FPR (fp_reg[31], f31, NULL, gcc_dwarf_f31_mips, gcc_dwarf_f31_mips, LLDB_INVALID_REGNUM, gdb_f31_mips), + DEFINE_FPR_INFO (fcsr, fcsr, NULL, gcc_dwarf_fcsr_mips, gcc_dwarf_fcsr_mips, LLDB_INVALID_REGNUM, gdb_fcsr_mips), + DEFINE_FPR_INFO (fir, fir, NULL, gcc_dwarf_fir_mips, gcc_dwarf_fir_mips, LLDB_INVALID_REGNUM, gdb_fir_mips) +}; +static_assert((sizeof(g_register_infos_mips) / sizeof(g_register_infos_mips[0])) == k_num_registers_mips, + "g_register_infos_mips has wrong number of register infos"); + +#undef GPR_OFFSET +#undef FPR_OFFSET +#undef DEFINE_GPR +#undef DEFINE_FPR + +#endif // DECLARE_REGISTER_INFOS_MIPS_STRUCT diff --git a/source/Plugins/Process/Utility/RegisterInfos_mips64.h b/source/Plugins/Process/Utility/RegisterInfos_mips64.h index 187b8e98332e..e03aea4e3f2c 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_mips64.h +++ b/source/Plugins/Process/Utility/RegisterInfos_mips64.h @@ -6,20 +6,31 @@ // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// +#include "llvm/Support/Compiler.h" #include <stddef.h> +#ifdef DECLARE_REGISTER_INFOS_MIPS64_STRUCT + // Computes the offset of the given GPR in the user data area. -#define GPR_OFFSET(regname) \ - (offsetof(GPR, regname)) +#define GPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(GPR, regname)) -#ifdef DECLARE_REGISTER_INFOS_MIPS64_STRUCT +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + LLVM_EXTENSION offsetof(FPR_mips, regname) \ + +// RegisterKind: GCC, DWARF, Generic, GDB, LLDB // Note that the size and offset will be updated by platform-specific classes. -#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((GPR*)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, \ +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((GPR*)0)->reg), GPR_OFFSET(reg), eEncodingUint, \ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips64 }, NULL, NULL } +#define DEFINE_FPR(member, reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_mips*)0)->member), FPR_OFFSET(member), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips64 }, NULL, NULL } + static RegisterInfo g_register_infos_mips64[] = { @@ -28,14 +39,14 @@ g_register_infos_mips64[] = DEFINE_GPR(r1, NULL, gcc_dwarf_r1_mips64, gcc_dwarf_r1_mips64, LLDB_INVALID_REGNUM, gdb_r1_mips64), DEFINE_GPR(r2, NULL, gcc_dwarf_r2_mips64, gcc_dwarf_r2_mips64, LLDB_INVALID_REGNUM, gdb_r2_mips64), DEFINE_GPR(r3, NULL, gcc_dwarf_r3_mips64, gcc_dwarf_r3_mips64, LLDB_INVALID_REGNUM, gdb_r3_mips64), - DEFINE_GPR(r4, NULL, gcc_dwarf_r4_mips64, gcc_dwarf_r4_mips64, LLDB_INVALID_REGNUM, gdb_r4_mips64), - DEFINE_GPR(r5, NULL, gcc_dwarf_r5_mips64, gcc_dwarf_r5_mips64, LLDB_INVALID_REGNUM, gdb_r5_mips64), - DEFINE_GPR(r6, NULL, gcc_dwarf_r6_mips64, gcc_dwarf_r6_mips64, LLDB_INVALID_REGNUM, gdb_r6_mips64), - DEFINE_GPR(r7, NULL, gcc_dwarf_r7_mips64, gcc_dwarf_r7_mips64, LLDB_INVALID_REGNUM, gdb_r7_mips64), - DEFINE_GPR(r8, NULL, gcc_dwarf_r8_mips64, gcc_dwarf_r8_mips64, LLDB_INVALID_REGNUM, gdb_r8_mips64), - DEFINE_GPR(r9, NULL, gcc_dwarf_r9_mips64, gcc_dwarf_r9_mips64, LLDB_INVALID_REGNUM, gdb_r9_mips64), - DEFINE_GPR(r10, NULL, gcc_dwarf_r10_mips64, gcc_dwarf_r10_mips64, LLDB_INVALID_REGNUM, gdb_r10_mips64), - DEFINE_GPR(r11, NULL, gcc_dwarf_r11_mips64, gcc_dwarf_r11_mips64, LLDB_INVALID_REGNUM, gdb_r11_mips64), + DEFINE_GPR(r4, NULL, gcc_dwarf_r4_mips64, gcc_dwarf_r4_mips64, LLDB_REGNUM_GENERIC_ARG1, gdb_r4_mips64), + DEFINE_GPR(r5, NULL, gcc_dwarf_r5_mips64, gcc_dwarf_r5_mips64, LLDB_REGNUM_GENERIC_ARG2, gdb_r5_mips64), + DEFINE_GPR(r6, NULL, gcc_dwarf_r6_mips64, gcc_dwarf_r6_mips64, LLDB_REGNUM_GENERIC_ARG3, gdb_r6_mips64), + DEFINE_GPR(r7, NULL, gcc_dwarf_r7_mips64, gcc_dwarf_r7_mips64, LLDB_REGNUM_GENERIC_ARG4, gdb_r7_mips64), + DEFINE_GPR(r8, NULL, gcc_dwarf_r8_mips64, gcc_dwarf_r8_mips64, LLDB_REGNUM_GENERIC_ARG5, gdb_r8_mips64), + DEFINE_GPR(r9, NULL, gcc_dwarf_r9_mips64, gcc_dwarf_r9_mips64, LLDB_REGNUM_GENERIC_ARG6, gdb_r9_mips64), + DEFINE_GPR(r10, NULL, gcc_dwarf_r10_mips64, gcc_dwarf_r10_mips64, LLDB_REGNUM_GENERIC_ARG7, gdb_r10_mips64), + DEFINE_GPR(r11, NULL, gcc_dwarf_r11_mips64, gcc_dwarf_r11_mips64, LLDB_REGNUM_GENERIC_ARG8, gdb_r11_mips64), DEFINE_GPR(r12, NULL, gcc_dwarf_r12_mips64, gcc_dwarf_r12_mips64, LLDB_INVALID_REGNUM, gdb_r12_mips64), DEFINE_GPR(r13, NULL, gcc_dwarf_r13_mips64, gcc_dwarf_r13_mips64, LLDB_INVALID_REGNUM, gdb_r13_mips64), DEFINE_GPR(r14, NULL, gcc_dwarf_r14_mips64, gcc_dwarf_r14_mips64, LLDB_INVALID_REGNUM, gdb_r14_mips64), @@ -54,23 +65,58 @@ g_register_infos_mips64[] = DEFINE_GPR(r27, NULL, gcc_dwarf_r27_mips64, gcc_dwarf_r27_mips64, LLDB_INVALID_REGNUM, gdb_r27_mips64), DEFINE_GPR(gp, "r28", gcc_dwarf_gp_mips64, gcc_dwarf_gp_mips64, LLDB_INVALID_REGNUM, gdb_gp_mips64), DEFINE_GPR(sp, "r29", gcc_dwarf_sp_mips64, gcc_dwarf_sp_mips64, LLDB_REGNUM_GENERIC_SP, gdb_sp_mips64), - DEFINE_GPR(r30, NULL, gcc_dwarf_r30_mips64, gcc_dwarf_r30_mips64, LLDB_INVALID_REGNUM, gdb_r30_mips64), - DEFINE_GPR(ra, "r31", gcc_dwarf_ra_mips64, gcc_dwarf_ra_mips64, LLDB_INVALID_REGNUM, gdb_ra_mips64), - DEFINE_GPR(sr, NULL, gcc_dwarf_sr_mips64, gcc_dwarf_sr_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r30, NULL, gcc_dwarf_r30_mips64, gcc_dwarf_r30_mips64, LLDB_REGNUM_GENERIC_FP, gdb_r30_mips64), + DEFINE_GPR(ra, "r31", gcc_dwarf_ra_mips64, gcc_dwarf_ra_mips64, LLDB_REGNUM_GENERIC_RA, gdb_ra_mips64), DEFINE_GPR(mullo, NULL, gcc_dwarf_lo_mips64, gcc_dwarf_lo_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_GPR(mulhi, NULL, gcc_dwarf_hi_mips64, gcc_dwarf_hi_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, "pc", gcc_dwarf_pc_mips64, gcc_dwarf_pc_mips64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), DEFINE_GPR(badvaddr, NULL, gcc_dwarf_bad_mips64, gcc_dwarf_bad_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(sr, NULL, gcc_dwarf_sr_mips64, gcc_dwarf_sr_mips64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), DEFINE_GPR(cause, NULL, gcc_dwarf_cause_mips64, gcc_dwarf_cause_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_GPR(pc, "pc", gcc_dwarf_pc_mips64, gcc_dwarf_pc_mips64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), DEFINE_GPR(ic, NULL, gcc_dwarf_ic_mips64, gcc_dwarf_ic_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_GPR(dummy, NULL, gcc_dwarf_dummy_mips64, gcc_dwarf_dummy_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + DEFINE_FPR (fp_reg[0], f0, NULL, gcc_dwarf_f0_mips64, gcc_dwarf_f0_mips64, LLDB_INVALID_REGNUM, gdb_f0_mips64), + DEFINE_FPR (fp_reg[1], f1, NULL, gcc_dwarf_f1_mips64, gcc_dwarf_f1_mips64, LLDB_INVALID_REGNUM, gdb_f1_mips64), + DEFINE_FPR (fp_reg[2], f2, NULL, gcc_dwarf_f2_mips64, gcc_dwarf_f2_mips64, LLDB_INVALID_REGNUM, gdb_f2_mips64), + DEFINE_FPR (fp_reg[3], f3, NULL, gcc_dwarf_f3_mips64, gcc_dwarf_f3_mips64, LLDB_INVALID_REGNUM, gdb_f3_mips64), + DEFINE_FPR (fp_reg[4], f4, NULL, gcc_dwarf_f4_mips64, gcc_dwarf_f4_mips64, LLDB_INVALID_REGNUM, gdb_f4_mips64), + DEFINE_FPR (fp_reg[5], f5, NULL, gcc_dwarf_f5_mips64, gcc_dwarf_f5_mips64, LLDB_INVALID_REGNUM, gdb_f5_mips64), + DEFINE_FPR (fp_reg[6], f6, NULL, gcc_dwarf_f6_mips64, gcc_dwarf_f6_mips64, LLDB_INVALID_REGNUM, gdb_f6_mips64), + DEFINE_FPR (fp_reg[7], f7, NULL, gcc_dwarf_f7_mips64, gcc_dwarf_f7_mips64, LLDB_INVALID_REGNUM, gdb_f7_mips64), + DEFINE_FPR (fp_reg[8], f8, NULL, gcc_dwarf_f8_mips64, gcc_dwarf_f8_mips64, LLDB_INVALID_REGNUM, gdb_f8_mips64), + DEFINE_FPR (fp_reg[9], f9, NULL, gcc_dwarf_f9_mips64, gcc_dwarf_f9_mips64, LLDB_INVALID_REGNUM, gdb_f9_mips64), + DEFINE_FPR (fp_reg[10], f10, NULL, gcc_dwarf_f10_mips64, gcc_dwarf_f10_mips64, LLDB_INVALID_REGNUM, gdb_f10_mips64), + DEFINE_FPR (fp_reg[11], f11, NULL, gcc_dwarf_f11_mips64, gcc_dwarf_f11_mips64, LLDB_INVALID_REGNUM, gdb_f11_mips64), + DEFINE_FPR (fp_reg[12], f12, NULL, gcc_dwarf_f12_mips64, gcc_dwarf_f12_mips64, LLDB_INVALID_REGNUM, gdb_f12_mips64), + DEFINE_FPR (fp_reg[13], f13, NULL, gcc_dwarf_f13_mips64, gcc_dwarf_f13_mips64, LLDB_INVALID_REGNUM, gdb_f13_mips64), + DEFINE_FPR (fp_reg[14], f14, NULL, gcc_dwarf_f14_mips64, gcc_dwarf_f14_mips64, LLDB_INVALID_REGNUM, gdb_f14_mips64), + DEFINE_FPR (fp_reg[15], f15, NULL, gcc_dwarf_f15_mips64, gcc_dwarf_f15_mips64, LLDB_INVALID_REGNUM, gdb_f15_mips64), + DEFINE_FPR (fp_reg[16], f16, NULL, gcc_dwarf_f16_mips64, gcc_dwarf_f16_mips64, LLDB_INVALID_REGNUM, gdb_f16_mips64), + DEFINE_FPR (fp_reg[17], f17, NULL, gcc_dwarf_f17_mips64, gcc_dwarf_f17_mips64, LLDB_INVALID_REGNUM, gdb_f17_mips64), + DEFINE_FPR (fp_reg[18], f18, NULL, gcc_dwarf_f18_mips64, gcc_dwarf_f18_mips64, LLDB_INVALID_REGNUM, gdb_f18_mips64), + DEFINE_FPR (fp_reg[19], f19, NULL, gcc_dwarf_f19_mips64, gcc_dwarf_f19_mips64, LLDB_INVALID_REGNUM, gdb_f19_mips64), + DEFINE_FPR (fp_reg[20], f20, NULL, gcc_dwarf_f20_mips64, gcc_dwarf_f20_mips64, LLDB_INVALID_REGNUM, gdb_f20_mips64), + DEFINE_FPR (fp_reg[21], f21, NULL, gcc_dwarf_f21_mips64, gcc_dwarf_f21_mips64, LLDB_INVALID_REGNUM, gdb_f21_mips64), + DEFINE_FPR (fp_reg[22], f22, NULL, gcc_dwarf_f22_mips64, gcc_dwarf_f22_mips64, LLDB_INVALID_REGNUM, gdb_f22_mips64), + DEFINE_FPR (fp_reg[23], f23, NULL, gcc_dwarf_f23_mips64, gcc_dwarf_f23_mips64, LLDB_INVALID_REGNUM, gdb_f23_mips64), + DEFINE_FPR (fp_reg[24], f24, NULL, gcc_dwarf_f24_mips64, gcc_dwarf_f24_mips64, LLDB_INVALID_REGNUM, gdb_f24_mips64), + DEFINE_FPR (fp_reg[25], f25, NULL, gcc_dwarf_f25_mips64, gcc_dwarf_f25_mips64, LLDB_INVALID_REGNUM, gdb_f25_mips64), + DEFINE_FPR (fp_reg[26], f26, NULL, gcc_dwarf_f26_mips64, gcc_dwarf_f26_mips64, LLDB_INVALID_REGNUM, gdb_f26_mips64), + DEFINE_FPR (fp_reg[27], f27, NULL, gcc_dwarf_f27_mips64, gcc_dwarf_f27_mips64, LLDB_INVALID_REGNUM, gdb_f27_mips64), + DEFINE_FPR (fp_reg[28], f28, NULL, gcc_dwarf_f28_mips64, gcc_dwarf_f28_mips64, LLDB_INVALID_REGNUM, gdb_f28_mips64), + DEFINE_FPR (fp_reg[29], f29, NULL, gcc_dwarf_f29_mips64, gcc_dwarf_f29_mips64, LLDB_INVALID_REGNUM, gdb_f29_mips64), + DEFINE_FPR (fp_reg[30], f30, NULL, gcc_dwarf_f30_mips64, gcc_dwarf_f30_mips64, LLDB_INVALID_REGNUM, gdb_f30_mips64), + DEFINE_FPR (fp_reg[31], f31, NULL, gcc_dwarf_f31_mips64, gcc_dwarf_f31_mips64, LLDB_INVALID_REGNUM, gdb_f31_mips64), + DEFINE_FPR (fcsr, fcsr, NULL, gcc_dwarf_fcsr_mips64, gcc_dwarf_fcsr_mips64, LLDB_INVALID_REGNUM, gdb_fcsr_mips64), + DEFINE_FPR (fir, fir, NULL, gcc_dwarf_fir_mips64, gcc_dwarf_fir_mips64, LLDB_INVALID_REGNUM, gdb_fir_mips64) }; static_assert((sizeof(g_register_infos_mips64) / sizeof(g_register_infos_mips64[0])) == k_num_registers_mips64, "g_register_infos_mips64 has wrong number of register infos"); #undef DEFINE_GPR - -#endif // DECLARE_REGISTER_INFOS_MIPS64_STRUCT - +#undef DEFINE_FPR #undef GPR_OFFSET +#undef FPR_OFFSET +#endif // DECLARE_REGISTER_INFOS_MIPS64_STRUCT diff --git a/source/Plugins/Process/Utility/RegisterInfos_x86_64.h b/source/Plugins/Process/Utility/RegisterInfos_x86_64.h index c1bcd27053c6..5da74ff83487 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_x86_64.h +++ b/source/Plugins/Process/Utility/RegisterInfos_x86_64.h @@ -16,12 +16,17 @@ // Computes the offset of the given FPR in the extended data area. #define FPR_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(FPR, xstate) + \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xstate) + \ LLVM_EXTENSION offsetof(FXSAVE, regname)) // Computes the offset of the YMM register assembled from register halves. -#define YMM_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(YMM, regname)) +// Based on DNBArchImplX86_64.cpp from debugserver +#define YMM_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xstate) + \ + LLVM_EXTENSION offsetof(XSAVE, ymmh[0]) + \ + (32 * reg_index)) #ifdef DECLARE_REGISTER_INFOS_X86_64_STRUCT @@ -37,6 +42,8 @@ // Number of bytes needed to represent a YMM register. #define YMM_SIZE sizeof(YMMReg) +#define DR_SIZE sizeof(((DBG*)NULL)->dr[0]) + // RegisterKind: GCC, DWARF, Generic, GDB, LLDB // Note that the size and offset will be updated by platform-specific classes. @@ -67,7 +74,7 @@ NULL, NULL } #define DEFINE_YMM(reg, i) \ - { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(reg[i]), \ + { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(i), \ eEncodingVector, eFormatVectorOfUInt8, \ { gcc_dwarf_##reg##i##h_x86_64, gcc_dwarf_##reg##i##h_x86_64, LLDB_INVALID_REGNUM, gdb_##reg##i##h_x86_64, lldb_##reg##i##_x86_64 }, \ NULL, NULL } @@ -298,7 +305,7 @@ do { #define UPDATE_YMM_INFO(reg, i) \ do { \ - g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(reg[i]); \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(i); \ } while(false); #define UPDATE_DR_INFO(reg_index) \ diff --git a/source/Plugins/Process/Utility/StopInfoMachException.cpp b/source/Plugins/Process/Utility/StopInfoMachException.cpp index a69b38b6c93e..7c68d0d07821 100644 --- a/source/Plugins/Process/Utility/StopInfoMachException.cpp +++ b/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -498,12 +498,15 @@ StopInfoMachException::CreateStopReasonWithMachException // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. - if (bp_site_sp->ValidForThisThread (&thread)) + // If we have an operating system plug-in, we might have set a thread specific breakpoint using the + // operating system thread ID, so we can't make any assumptions about the thread ID so we must always + // report the breakpoint regardless of the thread. + if (bp_site_sp->ValidForThisThread (&thread) || thread.GetProcess()->GetOperatingSystem () != NULL) return StopInfo::CreateStopReasonWithBreakpointSiteID (thread, bp_site_sp->GetID()); else return StopInfoSP(); } - + // Don't call this a trace if we weren't single stepping this thread. if (is_trace_if_actual_breakpoint_missing && thread.GetTemporaryResumeState() == eStateStepping) { diff --git a/source/Plugins/Process/Utility/ThreadMemory.cpp b/source/Plugins/Process/Utility/ThreadMemory.cpp index 56e5a9a59fab..6a7aa626bafc 100644 --- a/source/Plugins/Process/Utility/ThreadMemory.cpp +++ b/source/Plugins/Process/Utility/ThreadMemory.cpp @@ -105,7 +105,7 @@ ThreadMemory::CalculateStopInfo () if (m_backing_thread_sp) { lldb::StopInfoSP backing_stop_info_sp (m_backing_thread_sp->GetPrivateStopInfo()); - if (backing_stop_info_sp) + if (backing_stop_info_sp && backing_stop_info_sp->IsValidForOperatingSystemThread(*this)) { backing_stop_info_sp->SetThread (shared_from_this()); SetStopInfo (backing_stop_info_sp); diff --git a/source/Plugins/Process/Utility/UnwindLLDB.cpp b/source/Plugins/Process/Utility/UnwindLLDB.cpp index fc592e60d86d..02d3ecd7b3c5 100644 --- a/source/Plugins/Process/Utility/UnwindLLDB.cpp +++ b/source/Plugins/Process/Utility/UnwindLLDB.cpp @@ -12,6 +12,7 @@ #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/ABI.h" #include "lldb/Target/Thread.h" #include "lldb/Target/Target.h" #include "lldb/Target/Process.h" diff --git a/source/Plugins/Process/Utility/lldb-arm-register-enums.h b/source/Plugins/Process/Utility/lldb-arm-register-enums.h new file mode 100644 index 000000000000..a617f6550f65 --- /dev/null +++ b/source/Plugins/Process/Utility/lldb-arm-register-enums.h @@ -0,0 +1,153 @@ +//===-- lldb-arm-register-enums.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_arm_register_enums_h +#define lldb_arm_register_enums_h + +namespace lldb_private +{ + // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + + //--------------------------------------------------------------------------- + // Internal codes for all ARM registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_arm = 0, + gpr_r0_arm = k_first_gpr_arm, + gpr_r1_arm, + gpr_r2_arm, + gpr_r3_arm, + gpr_r4_arm, + gpr_r5_arm, + gpr_r6_arm, + gpr_r7_arm, + gpr_r8_arm, + gpr_r9_arm, + gpr_r10_arm, + gpr_r11_arm, + gpr_r12_arm, + gpr_r13_arm, gpr_sp_arm = gpr_r13_arm, + gpr_r14_arm, gpr_lr_arm = gpr_r14_arm, + gpr_r15_arm, gpr_pc_arm = gpr_r15_arm, + gpr_cpsr_arm, + + k_last_gpr_arm = gpr_cpsr_arm, + + k_first_fpr_arm, + fpu_s0_arm = k_first_fpr_arm, + fpu_s1_arm, + fpu_s2_arm, + fpu_s3_arm, + fpu_s4_arm, + fpu_s5_arm, + fpu_s6_arm, + fpu_s7_arm, + fpu_s8_arm, + fpu_s9_arm, + fpu_s10_arm, + fpu_s11_arm, + fpu_s12_arm, + fpu_s13_arm, + fpu_s14_arm, + fpu_s15_arm, + fpu_s16_arm, + fpu_s17_arm, + fpu_s18_arm, + fpu_s19_arm, + fpu_s20_arm, + fpu_s21_arm, + fpu_s22_arm, + fpu_s23_arm, + fpu_s24_arm, + fpu_s25_arm, + fpu_s26_arm, + fpu_s27_arm, + fpu_s28_arm, + fpu_s29_arm, + fpu_s30_arm, + fpu_s31_arm, + fpu_fpscr_arm, + k_last_fpr_arm = fpu_fpscr_arm, + exc_exception_arm, + exc_fsr_arm, + exc_far_arm, + + dbg_bvr0_arm, + dbg_bvr1_arm, + dbg_bvr2_arm, + dbg_bvr3_arm, + dbg_bvr4_arm, + dbg_bvr5_arm, + dbg_bvr6_arm, + dbg_bvr7_arm, + dbg_bvr8_arm, + dbg_bvr9_arm, + dbg_bvr10_arm, + dbg_bvr11_arm, + dbg_bvr12_arm, + dbg_bvr13_arm, + dbg_bvr14_arm, + dbg_bvr15_arm, + dbg_bcr0_arm, + dbg_bcr1_arm, + dbg_bcr2_arm, + dbg_bcr3_arm, + dbg_bcr4_arm, + dbg_bcr5_arm, + dbg_bcr6_arm, + dbg_bcr7_arm, + dbg_bcr8_arm, + dbg_bcr9_arm, + dbg_bcr10_arm, + dbg_bcr11_arm, + dbg_bcr12_arm, + dbg_bcr13_arm, + dbg_bcr14_arm, + dbg_bcr15_arm, + dbg_wvr0_arm, + dbg_wvr1_arm, + dbg_wvr2_arm, + dbg_wvr3_arm, + dbg_wvr4_arm, + dbg_wvr5_arm, + dbg_wvr6_arm, + dbg_wvr7_arm, + dbg_wvr8_arm, + dbg_wvr9_arm, + dbg_wvr10_arm, + dbg_wvr11_arm, + dbg_wvr12_arm, + dbg_wvr13_arm, + dbg_wvr14_arm, + dbg_wvr15_arm, + dbg_wcr0_arm, + dbg_wcr1_arm, + dbg_wcr2_arm, + dbg_wcr3_arm, + dbg_wcr4_arm, + dbg_wcr5_arm, + dbg_wcr6_arm, + dbg_wcr7_arm, + dbg_wcr8_arm, + dbg_wcr9_arm, + dbg_wcr10_arm, + dbg_wcr11_arm, + dbg_wcr12_arm, + dbg_wcr13_arm, + dbg_wcr14_arm, + dbg_wcr15_arm, + + k_num_registers_arm, + k_num_gpr_registers_arm = k_last_gpr_arm - k_first_gpr_arm + 1, + k_num_fpr_registers_arm = k_last_fpr_arm - k_first_fpr_arm + 1 + }; +} + +#endif // #ifndef lldb_arm64_register_enums_h diff --git a/source/Plugins/Process/Utility/lldb-arm64-register-enums.h b/source/Plugins/Process/Utility/lldb-arm64-register-enums.h new file mode 100644 index 000000000000..a0c0db0f2786 --- /dev/null +++ b/source/Plugins/Process/Utility/lldb-arm64-register-enums.h @@ -0,0 +1,172 @@ +//===-- lldb-arm64-register-enums.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_arm64_register_enums_h +#define lldb_arm64_register_enums_h + +namespace lldb_private +{ + // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + + //--------------------------------------------------------------------------- + // Internal codes for all ARM64 registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_arm64, + gpr_x0_arm64 = k_first_gpr_arm64, + gpr_x1_arm64, + gpr_x2_arm64, + gpr_x3_arm64, + gpr_x4_arm64, + gpr_x5_arm64, + gpr_x6_arm64, + gpr_x7_arm64, + gpr_x8_arm64, + gpr_x9_arm64, + gpr_x10_arm64, + gpr_x11_arm64, + gpr_x12_arm64, + gpr_x13_arm64, + gpr_x14_arm64, + gpr_x15_arm64, + gpr_x16_arm64, + gpr_x17_arm64, + gpr_x18_arm64, + gpr_x19_arm64, + gpr_x20_arm64, + gpr_x21_arm64, + gpr_x22_arm64, + gpr_x23_arm64, + gpr_x24_arm64, + gpr_x25_arm64, + gpr_x26_arm64, + gpr_x27_arm64, + gpr_x28_arm64, + gpr_fp_arm64, + gpr_lr_arm64, + gpr_sp_arm64, + gpr_pc_arm64, + gpr_cpsr_arm64, + + k_last_gpr_arm64 = gpr_cpsr_arm64, + + k_first_fpr_arm64, + fpu_v0_arm64 = k_first_fpr_arm64, + fpu_v1_arm64, + fpu_v2_arm64, + fpu_v3_arm64, + fpu_v4_arm64, + fpu_v5_arm64, + fpu_v6_arm64, + fpu_v7_arm64, + fpu_v8_arm64, + fpu_v9_arm64, + fpu_v10_arm64, + fpu_v11_arm64, + fpu_v12_arm64, + fpu_v13_arm64, + fpu_v14_arm64, + fpu_v15_arm64, + fpu_v16_arm64, + fpu_v17_arm64, + fpu_v18_arm64, + fpu_v19_arm64, + fpu_v20_arm64, + fpu_v21_arm64, + fpu_v22_arm64, + fpu_v23_arm64, + fpu_v24_arm64, + fpu_v25_arm64, + fpu_v26_arm64, + fpu_v27_arm64, + fpu_v28_arm64, + fpu_v29_arm64, + fpu_v30_arm64, + fpu_v31_arm64, + fpu_fpsr_arm64, + fpu_fpcr_arm64, + k_last_fpr_arm64 = fpu_fpcr_arm64, + + exc_far_arm64, + exc_esr_arm64, + exc_exception_arm64, + + dbg_bvr0_arm64, + dbg_bvr1_arm64, + dbg_bvr2_arm64, + dbg_bvr3_arm64, + dbg_bvr4_arm64, + dbg_bvr5_arm64, + dbg_bvr6_arm64, + dbg_bvr7_arm64, + dbg_bvr8_arm64, + dbg_bvr9_arm64, + dbg_bvr10_arm64, + dbg_bvr11_arm64, + dbg_bvr12_arm64, + dbg_bvr13_arm64, + dbg_bvr14_arm64, + dbg_bvr15_arm64, + dbg_bcr0_arm64, + dbg_bcr1_arm64, + dbg_bcr2_arm64, + dbg_bcr3_arm64, + dbg_bcr4_arm64, + dbg_bcr5_arm64, + dbg_bcr6_arm64, + dbg_bcr7_arm64, + dbg_bcr8_arm64, + dbg_bcr9_arm64, + dbg_bcr10_arm64, + dbg_bcr11_arm64, + dbg_bcr12_arm64, + dbg_bcr13_arm64, + dbg_bcr14_arm64, + dbg_bcr15_arm64, + dbg_wvr0_arm64, + dbg_wvr1_arm64, + dbg_wvr2_arm64, + dbg_wvr3_arm64, + dbg_wvr4_arm64, + dbg_wvr5_arm64, + dbg_wvr6_arm64, + dbg_wvr7_arm64, + dbg_wvr8_arm64, + dbg_wvr9_arm64, + dbg_wvr10_arm64, + dbg_wvr11_arm64, + dbg_wvr12_arm64, + dbg_wvr13_arm64, + dbg_wvr14_arm64, + dbg_wvr15_arm64, + dbg_wcr0_arm64, + dbg_wcr1_arm64, + dbg_wcr2_arm64, + dbg_wcr3_arm64, + dbg_wcr4_arm64, + dbg_wcr5_arm64, + dbg_wcr6_arm64, + dbg_wcr7_arm64, + dbg_wcr8_arm64, + dbg_wcr9_arm64, + dbg_wcr10_arm64, + dbg_wcr11_arm64, + dbg_wcr12_arm64, + dbg_wcr13_arm64, + dbg_wcr14_arm64, + dbg_wcr15_arm64, + + k_num_registers_arm64, + k_num_gpr_registers_arm64 = k_last_gpr_arm64 - k_first_gpr_arm64 + 1, + k_num_fpr_registers_arm64 = k_last_fpr_arm64 - k_first_fpr_arm64 + 1 + }; +} + +#endif // #ifndef lldb_arm64_register_enums_h diff --git a/source/Plugins/Process/Utility/lldb-mips64-register-enums.h b/source/Plugins/Process/Utility/lldb-mips64-register-enums.h new file mode 100644 index 000000000000..e8c93a6ec521 --- /dev/null +++ b/source/Plugins/Process/Utility/lldb-mips64-register-enums.h @@ -0,0 +1,199 @@ +//===-- lldb-mips64-register-enums.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_mips64_register_enums_h +#define lldb_mips64_register_enums_h + +namespace lldb_private +{ + // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + + //--------------------------------------------------------------------------- + // Internal codes for all mips registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_mips, + gpr_zero_mips = k_first_gpr_mips, + gpr_r1_mips, + gpr_r2_mips, + gpr_r3_mips, + gpr_r4_mips, + gpr_r5_mips, + gpr_r6_mips, + gpr_r7_mips, + gpr_r8_mips, + gpr_r9_mips, + gpr_r10_mips, + gpr_r11_mips, + gpr_r12_mips, + gpr_r13_mips, + gpr_r14_mips, + gpr_r15_mips, + gpr_r16_mips, + gpr_r17_mips, + gpr_r18_mips, + gpr_r19_mips, + gpr_r20_mips, + gpr_r21_mips, + gpr_r22_mips, + gpr_r23_mips, + gpr_r24_mips, + gpr_r25_mips, + gpr_r26_mips, + gpr_r27_mips, + gpr_gp_mips, + gpr_sp_mips, + gpr_r30_mips, + gpr_ra_mips, + gpr_mullo_mips, + gpr_mulhi_mips, + gpr_pc_mips, + gpr_badvaddr_mips, + gpr_sr_mips, + gpr_cause_mips, + + k_last_gpr_mips = gpr_cause_mips, + + k_first_fpr_mips, + fpr_f0_mips = k_first_fpr_mips, + fpr_f1_mips, + fpr_f2_mips, + fpr_f3_mips, + fpr_f4_mips, + fpr_f5_mips, + fpr_f6_mips, + fpr_f7_mips, + fpr_f8_mips, + fpr_f9_mips, + fpr_f10_mips, + fpr_f11_mips, + fpr_f12_mips, + fpr_f13_mips, + fpr_f14_mips, + fpr_f15_mips, + fpr_f16_mips, + fpr_f17_mips, + fpr_f18_mips, + fpr_f19_mips, + fpr_f20_mips, + fpr_f21_mips, + fpr_f22_mips, + fpr_f23_mips, + fpr_f24_mips, + fpr_f25_mips, + fpr_f26_mips, + fpr_f27_mips, + fpr_f28_mips, + fpr_f29_mips, + fpr_f30_mips, + fpr_f31_mips, + fpr_fcsr_mips, + fpr_fir_mips, + k_last_fpr_mips = fpr_fir_mips, + + k_num_registers_mips, + k_num_gpr_registers_mips = k_last_gpr_mips - k_first_gpr_mips + 1, + k_num_fpr_registers_mips = k_last_fpr_mips - k_first_fpr_mips + 1, + k_num_user_registers_mips = k_num_gpr_registers_mips + k_num_fpr_registers_mips, + }; + + //--------------------------------------------------------------------------- + // Internal codes for all mips64 registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_mips64, + gpr_zero_mips64 = k_first_gpr_mips64, + gpr_r1_mips64, + gpr_r2_mips64, + gpr_r3_mips64, + gpr_r4_mips64, + gpr_r5_mips64, + gpr_r6_mips64, + gpr_r7_mips64, + gpr_r8_mips64, + gpr_r9_mips64, + gpr_r10_mips64, + gpr_r11_mips64, + gpr_r12_mips64, + gpr_r13_mips64, + gpr_r14_mips64, + gpr_r15_mips64, + gpr_r16_mips64, + gpr_r17_mips64, + gpr_r18_mips64, + gpr_r19_mips64, + gpr_r20_mips64, + gpr_r21_mips64, + gpr_r22_mips64, + gpr_r23_mips64, + gpr_r24_mips64, + gpr_r25_mips64, + gpr_r26_mips64, + gpr_r27_mips64, + gpr_gp_mips64, + gpr_sp_mips64, + gpr_r30_mips64, + gpr_ra_mips64, + gpr_mullo_mips64, + gpr_mulhi_mips64, + gpr_pc_mips64, + gpr_badvaddr_mips64, + gpr_sr_mips64, + gpr_cause_mips64, + gpr_ic_mips64, + gpr_dummy_mips64, + + k_last_gpr_mips64 = gpr_dummy_mips64, + + k_first_fpr_mips64, + fpr_f0_mips64 = k_first_fpr_mips64, + fpr_f1_mips64, + fpr_f2_mips64, + fpr_f3_mips64, + fpr_f4_mips64, + fpr_f5_mips64, + fpr_f6_mips64, + fpr_f7_mips64, + fpr_f8_mips64, + fpr_f9_mips64, + fpr_f10_mips64, + fpr_f11_mips64, + fpr_f12_mips64, + fpr_f13_mips64, + fpr_f14_mips64, + fpr_f15_mips64, + fpr_f16_mips64, + fpr_f17_mips64, + fpr_f18_mips64, + fpr_f19_mips64, + fpr_f20_mips64, + fpr_f21_mips64, + fpr_f22_mips64, + fpr_f23_mips64, + fpr_f24_mips64, + fpr_f25_mips64, + fpr_f26_mips64, + fpr_f27_mips64, + fpr_f28_mips64, + fpr_f29_mips64, + fpr_f30_mips64, + fpr_f31_mips64, + fpr_fcsr_mips64, + fpr_fir_mips64, + k_last_fpr_mips64 = fpr_fir_mips64, + + k_num_registers_mips64, + k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1, + k_num_fpr_registers_mips64 = k_last_fpr_mips64 - k_first_fpr_mips64 + 1, + }; +} + +#endif // #ifndef fpr_mips64_register_enums_h diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/source/Plugins/Process/elf-core/ProcessElfCore.cpp index ead959508d88..2fff36dd4ee5 100644 --- a/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -10,6 +10,9 @@ // C Includes #include <stdlib.h> +// C++ Includes +#include <mutex> + // Other libraries and framework includes #include "lldb/Core/PluginManager.h" #include "lldb/Core/Module.h" @@ -191,7 +194,7 @@ ProcessElfCore::DoLoadCore () const uint32_t num_segments = core->GetProgramHeaderCount(); if (num_segments == 0) { - error.SetErrorString ("core file has no sections"); + error.SetErrorString ("core file has no segments"); return error; } @@ -374,13 +377,13 @@ ProcessElfCore::Clear() void ProcessElfCore::Initialize() { - static bool g_initialized = false; + static std::once_flag g_once_flag; - if (g_initialized == false) + std::call_once(g_once_flag, []() { - g_initialized = true; - PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); - } + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); } lldb::addr_t @@ -424,7 +427,8 @@ ParseFreeBSDPrStatus(ThreadData &thread_data, DataExtractor &data, ArchSpec &arch) { lldb::offset_t offset = 0; - bool lp64 = (arch.GetMachine() == llvm::Triple::mips64 || + bool lp64 = (arch.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::mips64 || arch.GetMachine() == llvm::Triple::ppc64 || arch.GetMachine() == llvm::Triple::x86_64); int pr_version = data.GetU32(&offset); diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.h b/source/Plugins/Process/elf-core/ProcessElfCore.h index 7988bee5ae53..775d9e94dd8e 100644 --- a/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -66,54 +66,42 @@ public: //------------------------------------------------------------------ // Check if a given Process //------------------------------------------------------------------ - virtual bool - CanDebug (lldb_private::Target &target, - bool plugin_specified_by_name) override; + bool CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override; //------------------------------------------------------------------ // Creating a new process, or attaching to an existing one //------------------------------------------------------------------ - virtual lldb_private::Error - DoLoadCore () override; + lldb_private::Error DoLoadCore() override; - virtual lldb_private::DynamicLoader * - GetDynamicLoader () override; + lldb_private::DynamicLoader *GetDynamicLoader() override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ - virtual lldb_private::ConstString - GetPluginName() override; + lldb_private::ConstString GetPluginName() override; - virtual uint32_t - GetPluginVersion() override; + uint32_t GetPluginVersion() override; //------------------------------------------------------------------ // Process Control //------------------------------------------------------------------ - virtual lldb_private::Error - DoDestroy () override; + lldb_private::Error DoDestroy() override; - virtual void - RefreshStateAfterStop() override; + void RefreshStateAfterStop() override; //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ - virtual bool - IsAlive () override; + bool IsAlive() override; //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ - virtual size_t - ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; - virtual size_t - DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; - virtual lldb::addr_t - GetImageInfoAddress () override; + lldb::addr_t GetImageInfoAddress() override; lldb_private::ArchSpec GetArchitecture(); @@ -126,9 +114,8 @@ protected: void Clear ( ); - virtual bool - UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list) override; + bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; private: //------------------------------------------------------------------ diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp new file mode 100644 index 000000000000..f046c112d8b6 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp @@ -0,0 +1,94 @@ +//===-- RegisterContextCorePOSIX_arm.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX.h" +#include "RegisterContextPOSIXCore_arm.h" + +using namespace lldb_private; + +RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm(Thread &thread, + RegisterInfoInterface *register_info, + const DataExtractor &gpregset, + const DataExtractor &fpregset) + : RegisterContextPOSIX_arm(thread, 0, register_info) +{ + m_gpr_buffer.reset(new DataBufferHeap(gpregset.GetDataStart(), gpregset.GetByteSize())); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() +{ +} + +bool +RegisterContextCorePOSIX_arm::ReadGPR() +{ + return true; +} + +bool +RegisterContextCorePOSIX_arm::ReadFPR() +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm::WriteGPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_arm::WriteFPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + lldb::offset_t offset = reg_info->byte_offset; + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) + { + value = v; + return true; + } + return false; +} + +bool +RegisterContextCorePOSIX_arm::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm::HardwareSingleStep(bool enable) +{ + return false; +} diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h new file mode 100644 index 000000000000..73e2ef7c3a93 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h @@ -0,0 +1,60 @@ +//===-- RegisterContextCorePOSIX_arm.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextCorePOSIX_arm_H_ +#define liblldb_RegisterContextCorePOSIX_arm_H_ + +#include "lldb/Core/DataBufferHeap.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h" + +class RegisterContextCorePOSIX_arm : + public RegisterContextPOSIX_arm +{ +public: + RegisterContextCorePOSIX_arm (lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + const lldb_private::DataExtractor &fpregset); + + ~RegisterContextCorePOSIX_arm(); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + bool + HardwareSingleStep(bool enable); + +protected: + bool + ReadGPR(); + + bool + ReadFPR(); + + bool + WriteGPR(); + + bool + WriteFPR(); + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb_private::DataExtractor m_gpr; +}; + +#endif // #ifndef liblldb_RegisterContextCorePOSIX_arm_H_ diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp new file mode 100644 index 000000000000..53c0c83c264a --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp @@ -0,0 +1,94 @@ +//===-- RegisterContextCorePOSIX_arm64.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX.h" +#include "RegisterContextPOSIXCore_arm64.h" + +using namespace lldb_private; + +RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(Thread &thread, + RegisterInfoInterface *register_info, + const DataExtractor &gpregset, + const DataExtractor &fpregset) + : RegisterContextPOSIX_arm64(thread, 0, register_info) +{ + m_gpr_buffer.reset(new DataBufferHeap(gpregset.GetDataStart(), gpregset.GetByteSize())); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_arm64::~RegisterContextCorePOSIX_arm64() +{ +} + +bool +RegisterContextCorePOSIX_arm64::ReadGPR() +{ + return true; +} + +bool +RegisterContextCorePOSIX_arm64::ReadFPR() +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm64::WriteGPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_arm64::WriteFPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + lldb::offset_t offset = reg_info->byte_offset; + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) + { + value = v; + return true; + } + return false; +} + +bool +RegisterContextCorePOSIX_arm64::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm64::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_arm64::HardwareSingleStep(bool enable) +{ + return false; +} diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h new file mode 100644 index 000000000000..2e1d6b4f9ca8 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h @@ -0,0 +1,60 @@ +//===-- RegisterContextCorePOSIX_arm64.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextCorePOSIX_arm64_H_ +#define liblldb_RegisterContextCorePOSIX_arm64_H_ + +#include "lldb/Core/DataBufferHeap.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h" + +class RegisterContextCorePOSIX_arm64 : + public RegisterContextPOSIX_arm64 +{ +public: + RegisterContextCorePOSIX_arm64 (lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + const lldb_private::DataExtractor &fpregset); + + ~RegisterContextCorePOSIX_arm64(); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + bool + HardwareSingleStep(bool enable); + +protected: + bool + ReadGPR(); + + bool + ReadFPR(); + + bool + WriteGPR(); + + bool + WriteFPR(); + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb_private::DataExtractor m_gpr; +}; + +#endif // #ifndef liblldb_RegisterContextCorePOSIX_arm64_H_ diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/source/Plugins/Process/elf-core/ThreadElfCore.cpp index b16335fb8e1e..2abb6ba5decd 100644 --- a/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -16,11 +16,17 @@ #include "ThreadElfCore.h" #include "ProcessElfCore.h" +#include "Plugins/Process/Utility/RegisterContextLinux_arm.h" +#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" #include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_arm.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_arm64.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" #include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" +#include "RegisterContextPOSIXCore_arm.h" +#include "RegisterContextPOSIXCore_arm64.h" #include "RegisterContextPOSIXCore_mips64.h" #include "RegisterContextPOSIXCore_powerpc.h" #include "RegisterContextPOSIXCore_x86_64.h" @@ -97,6 +103,12 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) { switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + reg_interface = new RegisterContextFreeBSD_arm64(arch); + break; + case llvm::Triple::arm: + reg_interface = new RegisterContextFreeBSD_arm(arch); + break; case llvm::Triple::ppc: reg_interface = new RegisterContextFreeBSD_powerpc32(arch); break; @@ -122,6 +134,12 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) { switch (arch.GetMachine()) { + case llvm::Triple::arm: + reg_interface = new RegisterContextLinux_arm(arch); + break; + case llvm::Triple::aarch64: + reg_interface = new RegisterContextLinux_arm64(arch); + break; case llvm::Triple::x86_64: reg_interface = new RegisterContextLinux_x86_64(arch); break; @@ -144,6 +162,12 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_arm64 (*this, reg_interface, m_gpregset_data, m_fpregset_data)); + break; + case llvm::Triple::arm: + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_arm (*this, reg_interface, m_gpregset_data, m_fpregset_data)); + break; case llvm::Triple::mips64: m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_mips64 (*this, reg_interface, m_gpregset_data, m_fpregset_data)); break; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index d633b3eaf34c..1af3947a75f3 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -18,6 +18,7 @@ // C++ Includes // Other libraries and framework includes #include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/ConnectionFileDescriptor.h" @@ -38,11 +39,20 @@ #if defined(__APPLE__) # define DEBUGSERVER_BASENAME "debugserver" #else -# define DEBUGSERVER_BASENAME "lldb-gdbserver" +# define DEBUGSERVER_BASENAME "lldb-server" +#endif + +#if defined (HAVE_LIBCOMPRESSION) +#include <compression.h> +#endif + +#if defined (HAVE_LIBZ) +#include <zlib.h> #endif using namespace lldb; using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; GDBRemoteCommunication::History::History (uint32_t size) : m_packets(), @@ -93,7 +103,7 @@ GDBRemoteCommunication::History::AddPacket (const std::string &src, } void -GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const +GDBRemoteCommunication::History::Dump (Stream &strm) const { const uint32_t size = GetNumPacketsInHistory (); const uint32_t first_idx = GetFirstSavedPacketIndex (); @@ -114,7 +124,7 @@ GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const } void -GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const +GDBRemoteCommunication::History::Dump (Log *log) const { if (log && !m_dumped_to_log) { @@ -142,20 +152,21 @@ GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const // GDBRemoteCommunication constructor //---------------------------------------------------------------------- GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, - const char *listener_name, - bool is_platform) : + const char *listener_name) : Communication(comm_name), #ifdef LLDB_CONFIGURATION_DEBUG m_packet_timeout (1000), #else m_packet_timeout (1), #endif + m_echo_number(0), + m_supports_qEcho (eLazyBoolCalculate), m_sequence_mutex (Mutex::eMutexTypeRecursive), m_public_is_running (false), m_private_is_running (false), m_history (512), m_send_acks (true), - m_is_platform (is_platform), + m_compression_type (CompressionType::None), m_listen_url () { } @@ -169,6 +180,12 @@ GDBRemoteCommunication::~GDBRemoteCommunication() { Disconnect(); } + + // Stop the communications read thread which is used to parse all + // incoming packets. This function will block until the read + // thread returns. + if (m_read_thread_enabled) + StopReadThread(); } char @@ -260,7 +277,7 @@ GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_le strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, (int)binary_start_offset, packet_data); const uint8_t *p; // Print binary data exactly as sent - for (p = (uint8_t*)packet_data + binary_start_offset; *p != '#'; ++p) + for (p = (const uint8_t*)packet_data + binary_start_offset; *p != '#'; ++p) strm.Printf("\\x%2.2x", *p); // Print the checksum strm.Printf("%*s", (int)3, p); @@ -293,7 +310,7 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunication::GetAck () { StringExtractorGDBRemote packet; - PacketResult result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ()); + PacketResult result = ReadPacket (packet, GetPacketTimeoutInMicroSeconds (), false); if (result == PacketResult::Success) { if (packet.GetResponseType() == StringExtractorGDBRemote::ResponseType::eAck) @@ -322,7 +339,63 @@ GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) } GDBRemoteCommunication::PacketResult -GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec) +GDBRemoteCommunication::ReadPacket (StringExtractorGDBRemote &response, uint32_t timeout_usec, bool sync_on_timeout) +{ + if (m_read_thread_enabled) + return PopPacketFromQueue (response, timeout_usec); + else + return WaitForPacketWithTimeoutMicroSecondsNoLock (response, timeout_usec, sync_on_timeout); +} + + +// This function is called when a packet is requested. +// A whole packet is popped from the packet queue and returned to the caller. +// Packets are placed into this queue from the communication read thread. +// See GDBRemoteCommunication::AppendBytesToCache. +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::PopPacketFromQueue (StringExtractorGDBRemote &response, uint32_t timeout_usec) +{ + // Calculate absolute timeout value + TimeValue timeout = TimeValue::Now(); + timeout.OffsetWithMicroSeconds(timeout_usec); + + do + { + // scope for the mutex + { + // lock down the packet queue + Mutex::Locker locker(m_packet_queue_mutex); + + // Wait on condition variable. + if (m_packet_queue.size() == 0) + m_condition_queue_not_empty.Wait(m_packet_queue_mutex, &timeout); + + if (m_packet_queue.size() > 0) + { + // get the front element of the queue + response = m_packet_queue.front(); + + // remove the front element + m_packet_queue.pop(); + + // we got a packet + return PacketResult::Success; + } + } + + // Disconnected + if (!IsConnected()) + return PacketResult::ErrorDisconnected; + + // Loop while not timed out + } while (TimeValue::Now() < timeout); + + return PacketResult::ErrorReplyTimeout; +} + + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec, bool sync_on_timeout) { uint8_t buffer[8192]; Error error; @@ -330,7 +403,7 @@ GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtrac Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE)); // Check for a packet from our cache first without trying any reading... - if (CheckForPacket (NULL, 0, packet)) + if (CheckForPacket(NULL, 0, packet) != PacketType::Invalid) return PacketResult::Success; bool timed_out = false; @@ -350,7 +423,7 @@ GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtrac if (bytes_read > 0) { - if (CheckForPacket (buffer, bytes_read, packet)) + if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid) return PacketResult::Success; } else @@ -359,6 +432,104 @@ GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtrac { case eConnectionStatusTimedOut: case eConnectionStatusInterrupted: + if (sync_on_timeout) + { + //------------------------------------------------------------------ + /// Sync the remote GDB server and make sure we get a response that + /// corresponds to what we send. + /// + /// Sends a "qEcho" packet and makes sure it gets the exact packet + /// echoed back. If the qEcho packet isn't supported, we send a qC + /// packet and make sure we get a valid thread ID back. We use the + /// "qC" packet since its response if very unique: is responds with + /// "QC%x" where %x is the thread ID of the current thread. This + /// makes the response unique enough from other packet responses to + /// ensure we are back on track. + /// + /// This packet is needed after we time out sending a packet so we + /// can ensure that we are getting the response for the packet we + /// are sending. There are no sequence IDs in the GDB remote + /// protocol (there used to be, but they are not supported anymore) + /// so if you timeout sending packet "abc", you might then send + /// packet "cde" and get the response for the previous "abc" packet. + /// Many responses are "OK" or "" (unsupported) or "EXX" (error) so + /// many responses for packets can look like responses for other + /// packets. So if we timeout, we need to ensure that we can get + /// back on track. If we can't get back on track, we must + /// disconnect. + //------------------------------------------------------------------ + bool sync_success = false; + bool got_actual_response = false; + // We timed out, we need to sync back up with the + char echo_packet[32]; + int echo_packet_len = 0; + RegularExpression response_regex; + + if (m_supports_qEcho == eLazyBoolYes) + { + echo_packet_len = ::snprintf (echo_packet, sizeof(echo_packet), "qEcho:%u", ++m_echo_number); + std::string regex_str = "^"; + regex_str += echo_packet; + regex_str += "$"; + response_regex.Compile(regex_str.c_str()); + } + else + { + echo_packet_len = ::snprintf (echo_packet, sizeof(echo_packet), "qC"); + response_regex.Compile("^QC[0-9A-Fa-f]+$"); + } + + PacketResult echo_packet_result = SendPacketNoLock (echo_packet, echo_packet_len); + if (echo_packet_result == PacketResult::Success) + { + const uint32_t max_retries = 3; + uint32_t successful_responses = 0; + for (uint32_t i=0; i<max_retries; ++i) + { + StringExtractorGDBRemote echo_response; + echo_packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (echo_response, timeout_usec, false); + if (echo_packet_result == PacketResult::Success) + { + ++successful_responses; + if (response_regex.Execute(echo_response.GetStringRef().c_str())) + { + sync_success = true; + break; + } + else if (successful_responses == 1) + { + // We got something else back as the first successful response, it probably is + // the response to the packet we actually wanted, so copy it over if this + // is the first success and continue to try to get the qEcho response + packet = echo_response; + got_actual_response = true; + } + } + else if (echo_packet_result == PacketResult::ErrorReplyTimeout) + continue; // Packet timed out, continue waiting for a response + else + break; // Something else went wrong getting the packet back, we failed and are done trying + } + } + + // We weren't able to sync back up with the server, we must abort otherwise + // all responses might not be from the right packets... + if (sync_success) + { + // We timed out, but were able to recover + if (got_actual_response) + { + // We initially timed out, but we did get a response that came in before the successful + // reply to our qEcho packet, so lets say everything is fine... + return PacketResult::Success; + } + } + else + { + disconnected = true; + Disconnect(); + } + } timed_out = true; break; case eConnectionStatusSuccess: @@ -385,6 +556,226 @@ GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtrac } bool +GDBRemoteCommunication::DecompressPacket () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + + if (!CompressionIsEnabled()) + return true; + + size_t pkt_size = m_bytes.size(); + if (pkt_size < 6) + return true; + if (m_bytes[0] != '$' && m_bytes[0] != '%') + return true; + if (m_bytes[1] != 'C' && m_bytes[1] != 'N') + return true; + if (m_bytes[pkt_size - 3] != '#') + return true; + if (!::isxdigit (m_bytes[pkt_size - 2]) || !::isxdigit (m_bytes[pkt_size - 1])) + return true; + + size_t content_length = pkt_size - 5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars + size_t content_start = 2; // The first character of the compressed/not-compressed text of the packet + size_t hash_mark_idx = pkt_size - 3; // The '#' character marking the end of the packet + size_t checksum_idx = pkt_size - 2; // The first character of the two hex checksum characters + + // Compressed packets ("$C") start with a base10 number which is the size of the uncompressed payload, + // then a : and then the compressed data. e.g. $C1024:<binary>#00 + // Update content_start and content_length to only include the <binary> part of the packet. + + uint64_t decompressed_bufsize = ULONG_MAX; + if (m_bytes[1] == 'C') + { + size_t i = content_start; + while (i < hash_mark_idx && isdigit(m_bytes[i])) + i++; + if (i < hash_mark_idx && m_bytes[i] == ':') + { + i++; + content_start = i; + content_length = hash_mark_idx - content_start; + std::string bufsize_str (m_bytes.data() + 2, i - 2 - 1); + errno = 0; + decompressed_bufsize = ::strtoul (bufsize_str.c_str(), NULL, 10); + if (errno != 0 || decompressed_bufsize == ULONG_MAX) + { + m_bytes.erase (0, pkt_size); + return false; + } + } + } + + if (GetSendAcks ()) + { + char packet_checksum_cstr[3]; + packet_checksum_cstr[0] = m_bytes[checksum_idx]; + packet_checksum_cstr[1] = m_bytes[checksum_idx + 1]; + packet_checksum_cstr[2] = '\0'; + long packet_checksum = strtol (packet_checksum_cstr, NULL, 16); + + long actual_checksum = CalculcateChecksum (m_bytes.data() + 1, hash_mark_idx - 1); + bool success = packet_checksum == actual_checksum; + if (!success) + { + if (log) + log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", + (int)(pkt_size), + m_bytes.c_str(), + (uint8_t)packet_checksum, + (uint8_t)actual_checksum); + } + // Send the ack or nack if needed + if (!success) + { + SendNack(); + m_bytes.erase (0, pkt_size); + return false; + } + else + { + SendAck(); + } + } + + if (m_bytes[1] == 'N') + { + // This packet was not compressed -- delete the 'N' character at the + // start and the packet may be processed as-is. + m_bytes.erase(1, 1); + return true; + } + + // Reverse the gdb-remote binary escaping that was done to the compressed text to + // guard characters like '$', '#', '}', etc. + std::vector<uint8_t> unescaped_content; + unescaped_content.reserve (content_length); + size_t i = content_start; + while (i < hash_mark_idx) + { + if (m_bytes[i] == '}') + { + i++; + unescaped_content.push_back (m_bytes[i] ^ 0x20); + } + else + { + unescaped_content.push_back (m_bytes[i]); + } + i++; + } + + uint8_t *decompressed_buffer = nullptr; + size_t decompressed_bytes = 0; + + if (decompressed_bufsize != ULONG_MAX) + { + decompressed_buffer = (uint8_t *) malloc (decompressed_bufsize + 1); + if (decompressed_buffer == nullptr) + { + m_bytes.erase (0, pkt_size); + return false; + } + + } + +#if defined (HAVE_LIBCOMPRESSION) + // libcompression is weak linked so check that compression_decode_buffer() is available + if (compression_decode_buffer != NULL && + (m_compression_type == CompressionType::ZlibDeflate + || m_compression_type == CompressionType::LZFSE + || m_compression_type == CompressionType::LZ4)) + { + compression_algorithm compression_type; + if (m_compression_type == CompressionType::ZlibDeflate) + compression_type = COMPRESSION_ZLIB; + else if (m_compression_type == CompressionType::LZFSE) + compression_type = COMPRESSION_LZFSE; + else if (m_compression_type == CompressionType::LZ4) + compression_type = COMPRESSION_LZ4_RAW; + else if (m_compression_type == CompressionType::LZMA) + compression_type = COMPRESSION_LZMA; + + + // If we have the expected size of the decompressed payload, we can allocate + // the right-sized buffer and do it. If we don't have that information, we'll + // need to try decoding into a big buffer and if the buffer wasn't big enough, + // increase it and try again. + + if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr) + { + decompressed_bytes = compression_decode_buffer (decompressed_buffer, decompressed_bufsize + 10 , + (uint8_t*) unescaped_content.data(), + unescaped_content.size(), + NULL, + compression_type); + } + } +#endif + +#if defined (HAVE_LIBZ) + if (decompressed_bytes == 0 + && decompressed_bufsize != ULONG_MAX + && decompressed_buffer != nullptr + && m_compression_type == CompressionType::ZlibDeflate) + { + z_stream stream; + memset (&stream, 0, sizeof (z_stream)); + stream.next_in = (Bytef *) unescaped_content.data(); + stream.avail_in = (uInt) unescaped_content.size(); + stream.total_in = 0; + stream.next_out = (Bytef *) decompressed_buffer; + stream.avail_out = decompressed_bufsize; + stream.total_out = 0; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + + if (inflateInit2 (&stream, -15) == Z_OK) + { + int status = inflate (&stream, Z_NO_FLUSH); + inflateEnd (&stream); + if (status == Z_STREAM_END) + { + decompressed_bytes = stream.total_out; + } + } + } +#endif + + if (decompressed_bytes == 0 || decompressed_buffer == nullptr) + { + if (decompressed_buffer) + free (decompressed_buffer); + m_bytes.erase (0, pkt_size); + return false; + } + + std::string new_packet; + new_packet.reserve (decompressed_bytes + 6); + new_packet.push_back (m_bytes[0]); + new_packet.append ((const char *) decompressed_buffer, decompressed_bytes); + new_packet.push_back ('#'); + if (GetSendAcks ()) + { + uint8_t decompressed_checksum = CalculcateChecksum ((const char *) decompressed_buffer, decompressed_bytes); + char decompressed_checksum_str[3]; + snprintf (decompressed_checksum_str, 3, "%02x", decompressed_checksum); + new_packet.append (decompressed_checksum_str); + } + else + { + new_packet.push_back ('0'); + new_packet.push_back ('0'); + } + + m_bytes = new_packet; + + free (decompressed_buffer); + return true; +} + +GDBRemoteCommunication::PacketType GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet) { // Put the packet data into the buffer in a thread safe fashion @@ -406,6 +797,8 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri m_bytes.append ((const char *)src, src_len); } + bool isNotifyPacket = false; + // Parse up the packets into gdb remote packets if (!m_bytes.empty()) { @@ -417,6 +810,17 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri size_t total_length = 0; size_t checksum_idx = std::string::npos; + // Size of packet before it is decompressed, for logging purposes + size_t original_packet_size = m_bytes.size(); + if (CompressionIsEnabled()) + { + if (DecompressPacket() == false) + { + packet.Clear(); + return GDBRemoteCommunication::PacketType::Standard; + } + } + switch (m_bytes[0]) { case '+': // Look for ack @@ -425,6 +829,10 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri content_length = total_length = 1; // The command is one byte long... break; + case '%': // Async notify packet + isNotifyPacket = true; + // Intentional fall through + case '$': // Look for a standard gdb packet? { @@ -467,6 +875,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri case '+': case '-': case '\x03': + case '%': case '$': done = true; break; @@ -486,7 +895,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri if (content_length == std::string::npos) { packet.Clear(); - return false; + return GDBRemoteCommunication::PacketType::Invalid; } else if (total_length > 0) { @@ -495,12 +904,10 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri assert (content_length <= m_bytes.size()); assert (total_length <= m_bytes.size()); assert (content_length <= total_length); - const size_t content_end = content_start + content_length; + size_t content_end = content_start + content_length; bool success = true; std::string &packet_str = packet.GetStringRef(); - - if (log) { // If logging was just enabled and we have history, then dump out what @@ -524,7 +931,10 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri { StreamString strm; // Packet header... - strm.Printf("<%4" PRIu64 "> read packet: %c", (uint64_t)total_length, m_bytes[0]); + if (CompressionIsEnabled()) + strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c", (uint64_t) original_packet_size, (uint64_t)total_length, m_bytes[0]); + else + strm.Printf("<%4" PRIu64 "> read packet: %c", (uint64_t)total_length, m_bytes[0]); for (size_t i=content_start; i<content_end; ++i) { // Remove binary escaped bytes when displaying the packet... @@ -547,7 +957,10 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri } else { - log->Printf("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str()); + if (CompressionIsEnabled()) + log->Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s", (uint64_t) original_packet_size, (uint64_t)total_length, (int)(total_length), m_bytes.c_str()); + else + log->Printf("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str()); } } @@ -587,7 +1000,7 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri } } - if (m_bytes[0] == '$') + if (m_bytes[0] == '$' || m_bytes[0] == '%') { assert (checksum_idx < m_bytes.size()); if (::isxdigit (m_bytes[checksum_idx+0]) || @@ -625,11 +1038,15 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri m_bytes.erase(0, total_length); packet.SetFilePos(0); - return success; + + if (isNotifyPacket) + return GDBRemoteCommunication::PacketType::Notify; + else + return GDBRemoteCommunication::PacketType::Standard; } } packet.Clear(); - return false; + return GDBRemoteCommunication::PacketType::Invalid; } Error @@ -681,7 +1098,7 @@ GDBRemoteCommunication::ListenThread (lldb::thread_arg_t arg) Error GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, uint16_t in_port, - lldb_private::ProcessLaunchInfo &launch_info, + ProcessLaunchInfo &launch_info, uint16_t &out_port) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); @@ -741,10 +1158,15 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, Args &debugserver_args = launch_info.GetArguments(); debugserver_args.Clear(); char arg_cstr[PATH_MAX]; - + // Start args with "debugserver /file/path -r --" debugserver_args.AppendArgument(debugserver_path); +#if !defined(__APPLE__) + // First argument to lldb-server must be mode in which to run. + debugserver_args.AppendArgument("gdbserver"); +#endif + // If a host and port is supplied then use it char host_and_port[128]; if (hostname) @@ -766,28 +1188,42 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, } llvm::SmallString<PATH_MAX> named_pipe_path; - Pipe port_named_pipe; + Pipe port_pipe; - bool listen = false; - if (host_and_port[0]) + if (host_and_port[0] && in_port == 0) { // Create a temporary file to get the stdout/stderr and redirect the // output of the command into this file. We will later read this file // if all goes well and fill the data into "command_output_ptr" - if (in_port == 0) + // Binding to port zero, we need to figure out what port it ends up + // using using a named pipe... + error = port_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path); + if (error.Success()) { - // Binding to port zero, we need to figure out what port it ends up - // using using a named pipe... - error = port_named_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path); - if (error.Fail()) - return error; debugserver_args.AppendArgument("--named-pipe"); debugserver_args.AppendArgument(named_pipe_path.c_str()); } else { - listen = true; + if (log) + log->Printf("GDBRemoteCommunication::%s() " + "named pipe creation failed: %s", + __FUNCTION__, error.AsCString()); + // let's try an unnamed pipe + error = port_pipe.CreateNew(true); + if (error.Fail()) + { + if (log) + log->Printf("GDBRemoteCommunication::%s() " + "unnamed pipe creation failed: %s", + __FUNCTION__, error.AsCString()); + return error; + } + int write_fd = port_pipe.GetWriteFileDescriptor(); + debugserver_args.AppendArgument("--pipe"); + debugserver_args.AppendArgument(std::to_string(write_fd).c_str()); + launch_info.AppendCloseFileAction(port_pipe.GetReadFileDescriptor()); } } else @@ -796,7 +1232,11 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, // connect to us.. error = StartListenThread ("127.0.0.1", 0); if (error.Fail()) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s() unable to start listen thread: %s", __FUNCTION__, error.AsCString()); return error; + } ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection (); // Wait for 10 seconds to resolve the bound port @@ -813,6 +1253,8 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, else { error.SetErrorString ("failed to bind to port 0 on 127.0.0.1"); + if (log) + log->Printf ("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, error.AsCString()); return error; } } @@ -824,12 +1266,21 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, debugserver_args.AppendArgument(arg_cstr); } +#if defined(__APPLE__) const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); if (env_debugserver_log_flags) { ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); debugserver_args.AppendArgument(arg_cstr); } +#else + const char *env_debugserver_log_channels = getenv("LLDB_SERVER_LOG_CHANNELS"); + if (env_debugserver_log_channels) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-channels=%s", env_debugserver_log_channels); + debugserver_args.AppendArgument(arg_cstr); + } +#endif // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an env var doesn't come back. uint32_t env_var_index = 1; @@ -865,56 +1316,70 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, { if (named_pipe_path.size() > 0) { - error = port_named_pipe.OpenAsReader(named_pipe_path, false); + error = port_pipe.OpenAsReader(named_pipe_path, false); + if (error.Fail()) + if (log) + log->Printf("GDBRemoteCommunication::%s() " + "failed to open named pipe %s for reading: %s", + __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); + } + + if (port_pipe.CanWrite()) + port_pipe.CloseWriteFileDescriptor(); + if (port_pipe.CanRead()) + { + char port_cstr[256]; + port_cstr[0] = '\0'; + size_t num_bytes = sizeof(port_cstr); + // Read port from pipe with 10 second timeout. + error = port_pipe.ReadWithTimeout(port_cstr, num_bytes, + std::chrono::seconds{10}, num_bytes); if (error.Success()) { - char port_cstr[256]; - port_cstr[0] = '\0'; - size_t num_bytes = sizeof(port_cstr); - // Read port from pipe with 10 second timeout. - error = port_named_pipe.ReadWithTimeout(port_cstr, num_bytes, std::chrono::microseconds(10 * 1000000), num_bytes); - if (error.Success()) - { - assert (num_bytes > 0 && port_cstr[num_bytes-1] == '\0'); - out_port = StringConvert::ToUInt32(port_cstr, 0); - if (log) - log->Printf("GDBRemoteCommunication::%s() debugserver listens %u port", __FUNCTION__, out_port); - } - else - { - if (log) - log->Printf("GDBRemoteCommunication::%s() failed to read a port value from named pipe %s: %s", __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); - - } - port_named_pipe.Close(); + assert(num_bytes > 0 && port_cstr[num_bytes-1] == '\0'); + out_port = StringConvert::ToUInt32(port_cstr, 0); + if (log) + log->Printf("GDBRemoteCommunication::%s() " + "debugserver listens %u port", + __FUNCTION__, out_port); } else { if (log) - log->Printf("GDBRemoteCommunication::%s() failed to open named pipe %s for reading: %s", __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); + log->Printf("GDBRemoteCommunication::%s() " + "failed to read a port value from pipe %s: %s", + __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); + } - const auto err = port_named_pipe.Delete(named_pipe_path); + port_pipe.Close(); + } + + if (named_pipe_path.size() > 0) + { + const auto err = port_pipe.Delete(named_pipe_path); if (err.Fail()) { if (log) - log->Printf ("GDBRemoteCommunication::%s failed to delete pipe %s: %s", __FUNCTION__, named_pipe_path.c_str(), err.AsCString()); + log->Printf ("GDBRemoteCommunication::%s failed to delete pipe %s: %s", + __FUNCTION__, named_pipe_path.c_str(), err.AsCString()); } } - else if (listen) - { - - } - else - { - // Make sure we actually connect with the debugserver... - JoinListenThread(); - } + + // Make sure we actually connect with the debugserver... + JoinListenThread(); } } else { error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME ); } + + if (error.Fail()) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, error.AsCString()); + } + return error; } @@ -923,3 +1388,65 @@ GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump (strm); } + +GDBRemoteCommunication::ScopedTimeout::ScopedTimeout (GDBRemoteCommunication& gdb_comm, + uint32_t timeout) : + m_gdb_comm (gdb_comm) +{ + m_saved_timeout = m_gdb_comm.SetPacketTimeout (timeout); +} + +GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout () +{ + m_gdb_comm.SetPacketTimeout (m_saved_timeout); +} + +// This function is called via the Communications class read thread when bytes become available +// for this connection. This function will consume all incoming bytes and try to parse whole +// packets as they become available. Full packets are placed in a queue, so that all packet +// requests can simply pop from this queue. Async notification packets will be dispatched +// immediately to the ProcessGDBRemote Async thread via an event. +void GDBRemoteCommunication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, lldb::ConnectionStatus status) +{ + StringExtractorGDBRemote packet; + + while (true) + { + PacketType type = CheckForPacket(bytes, len, packet); + + // scrub the data so we do not pass it back to CheckForPacket + // on future passes of the loop + bytes = nullptr; + len = 0; + + // we may have received no packet so lets bail out + if (type == PacketType::Invalid) + break; + + if (type == PacketType::Standard) + { + // scope for the mutex + { + // lock down the packet queue + Mutex::Locker locker(m_packet_queue_mutex); + // push a new packet into the queue + m_packet_queue.push(packet); + // Signal condition variable that we have a packet + m_condition_queue_not_empty.Signal(); + + } + } + + if (type == PacketType::Notify) + { + // put this packet into an event + const char *pdata = packet.GetStringRef().c_str(); + + // as the communication class, we are a broadcaster and the + // async thread is tuned to listen to us + BroadcastEvent( + eBroadcastBitGdbReadThreadGotNotify, + new EventDataBytes(pdata)); + } + } +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h index ee2f3276513d..7379bb3aa09b 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -14,6 +14,7 @@ // C++ Includes #include <list> #include <string> +#include <queue> // Other libraries and framework includes // Project includes @@ -27,6 +28,9 @@ #include "Utility/StringExtractorGDBRemote.h" +namespace lldb_private { +namespace process_gdb_remote { + typedef enum { eStoppointInvalid = -1, @@ -37,16 +41,33 @@ typedef enum eWatchpointReadWrite } GDBStoppointType; +enum class CompressionType +{ + None = 0, // no compression + ZlibDeflate, // zlib's deflate compression scheme, requires zlib or Apple's libcompression + LZFSE, // an Apple compression scheme, requires Apple's libcompression + LZ4, // lz compression - called "lz4 raw" in libcompression terms, compat with https://code.google.com/p/lz4/ + LZMA, // Lempel–Ziv–Markov chain algorithm +}; + class ProcessGDBRemote; -class GDBRemoteCommunication : public lldb_private::Communication +class GDBRemoteCommunication : public Communication { public: enum { - eBroadcastBitRunPacketSent = kLoUserBroadcastBit + eBroadcastBitRunPacketSent = kLoUserBroadcastBit, + eBroadcastBitGdbReadThreadGotNotify = kLoUserBroadcastBit << 1 // Sent when we received a notify packet. }; - + + enum class PacketType + { + Invalid = 0, + Standard, + Notify + }; + enum class PacketResult { Success = 0, // Success @@ -59,12 +80,25 @@ public: ErrorDisconnected, // We were disconnected ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet request }; + + // Class to change the timeout for a given scope and restore it to the original value when the + // created ScopedTimeout object got out of scope + class ScopedTimeout + { + public: + ScopedTimeout (GDBRemoteCommunication& gdb_comm, uint32_t timeout); + ~ScopedTimeout (); + + private: + GDBRemoteCommunication& m_gdb_comm; + uint32_t m_saved_timeout; + }; + //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ GDBRemoteCommunication(const char *comm_name, - const char *listener_name, - bool is_platform); + const char *listener_name); virtual ~GDBRemoteCommunication(); @@ -83,9 +117,9 @@ public: size_t payload_length); bool - GetSequenceMutex (lldb_private::Mutex::Locker& locker, const char *failure_message = NULL); + GetSequenceMutex (Mutex::Locker& locker, const char *failure_message = NULL); - bool + PacketType CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet); @@ -126,20 +160,20 @@ public: uint32_t GetPacketTimeoutInMicroSeconds () const { - return m_packet_timeout * lldb_private::TimeValue::MicroSecPerSec; + return m_packet_timeout * TimeValue::MicroSecPerSec; } //------------------------------------------------------------------ // Start a debugserver instance on the current host using the // supplied connection URL. //------------------------------------------------------------------ - lldb_private::Error + Error StartDebugserverProcess (const char *hostname, uint16_t in_port, // If set to zero, then out_port will contain the bound port on exit - lldb_private::ProcessLaunchInfo &launch_info, + ProcessLaunchInfo &launch_info, uint16_t &out_port); void - DumpHistory(lldb_private::Stream &strm); + DumpHistory(Stream &strm); protected: @@ -196,10 +230,10 @@ protected: uint32_t bytes_transmitted); void - Dump (lldb_private::Stream &strm) const; + Dump (Stream &strm) const; void - Dump (lldb_private::Log *log) const; + Dump (Log *log) const; bool DidDumpToLog () const @@ -257,33 +291,59 @@ protected: size_t payload_length); PacketResult + ReadPacket (StringExtractorGDBRemote &response, uint32_t timeout_usec, bool sync_on_timeout); + + // Pop a packet from the queue in a thread safe manner + PacketResult + PopPacketFromQueue (StringExtractorGDBRemote &response, uint32_t timeout_usec); + + PacketResult WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &response, - uint32_t timeout_usec); + uint32_t timeout_usec, + bool sync_on_timeout); + + bool + WaitForNotRunningPrivate (const TimeValue *timeout_ptr); + + bool + CompressionIsEnabled () + { + return m_compression_type != CompressionType::None; + } + // If compression is enabled, decompress the packet in m_bytes and update + // m_bytes with the uncompressed version. + // Returns 'true' packet was decompressed and m_bytes is the now-decompressed text. + // Returns 'false' if unable to decompress or if the checksum was invalid. + // + // NB: Once the packet has been decompressed, checksum cannot be computed based + // on m_bytes. The checksum was for the compressed packet. bool - WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr); + DecompressPacket (); //------------------------------------------------------------------ // Classes that inherit from GDBRemoteCommunication can see and modify these //------------------------------------------------------------------ uint32_t m_packet_timeout; + uint32_t m_echo_number; + LazyBool m_supports_qEcho; #ifdef ENABLE_MUTEX_ERROR_CHECKING - lldb_private::TrackingMutex m_sequence_mutex; + TrackingMutex m_sequence_mutex; #else - lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time + Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time #endif - lldb_private::Predicate<bool> m_public_is_running; - lldb_private::Predicate<bool> m_private_is_running; + Predicate<bool> m_public_is_running; + Predicate<bool> m_private_is_running; History m_history; bool m_send_acks; bool m_is_platform; // Set to true if this class represents a platform, // false if this class represents a debug session for // a single process + CompressionType m_compression_type; - lldb_private::Error - StartListenThread (const char *hostname = "127.0.0.1", - uint16_t port = 0); + Error + StartListenThread (const char *hostname = "127.0.0.1", uint16_t port = 0); bool JoinListenThread (); @@ -291,8 +351,25 @@ protected: static lldb::thread_result_t ListenThread (lldb::thread_arg_t arg); + // GDB-Remote read thread + // . this thread constantly tries to read from the communication + // class and stores all packets received in a queue. The usual + // threads read requests simply pop packets off the queue in the + // usual order. + // This setup allows us to intercept and handle async packets, such + // as the notify packet. + + // This method is defined as part of communication.h + // when the read thread gets any bytes it will pass them on to this function + virtual void AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, lldb::ConnectionStatus status); + private: - lldb_private::HostThread m_listen_thread; + + std::queue<StringExtractorGDBRemote> m_packet_queue; // The packet queue + lldb_private::Mutex m_packet_queue_mutex; // Mutex for accessing queue + Condition m_condition_queue_not_empty; // Condition variable to wait for packets + + HostThread m_listen_thread; std::string m_listen_url; //------------------------------------------------------------------ @@ -301,4 +378,7 @@ private: DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunication); }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // liblldb_GDBRemoteCommunication_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 0f99688fc823..ae0a2f5e66c5 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -11,16 +11,19 @@ #include "GDBRemoteCommunicationClient.h" // C Includes +#include <math.h> #include <sys/stat.h> // C++ Includes #include <sstream> +#include <numeric> // Other libraries and framework includes #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "lldb/Interpreter/Args.h" #include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" @@ -30,7 +33,10 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/TimeValue.h" +#include "lldb/Symbol/Symbol.h" #include "lldb/Target/Target.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/UnixSignals.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" @@ -38,18 +44,19 @@ #include "ProcessGDBRemoteLog.h" #include "lldb/Host/Config.h" +#if defined (HAVE_LIBCOMPRESSION) +#include <compression.h> +#endif + using namespace lldb; using namespace lldb_private; - -#if defined(LLDB_DISABLE_POSIX) && !defined(SIGSTOP) -#define SIGSTOP 17 -#endif +using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- -GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : - GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet", is_platform), +GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : + GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet"), m_supports_not_sending_acks (eLazyBoolCalculate), m_supports_thread_suffix (eLazyBoolCalculate), m_supports_threads_in_stop_reply (eLazyBoolCalculate), @@ -77,6 +84,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_supports_qXfer_auxv_read (eLazyBoolCalculate), m_supports_qXfer_libraries_read (eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read (eLazyBoolCalculate), + m_supports_qXfer_features_read (eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read (eLazyBoolCalculate), m_supports_jThreadExtendedInfo (eLazyBoolCalculate), m_supports_qProcessInfoPID (true), @@ -91,6 +99,8 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_supports_z4 (true), m_supports_QEnvironment (true), m_supports_QEnvironmentHexEncoded (true), + m_supports_qSymbol (true), + m_supports_jThreadsInfo (true), m_curr_pid (LLDB_INVALID_PROCESS_ID), m_curr_tid (LLDB_INVALID_THREAD_ID), m_curr_tid_run (LLDB_INVALID_THREAD_ID), @@ -130,7 +140,7 @@ GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() bool GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) { - ResetDiscoverableSettings(); + ResetDiscoverableSettings(false); // Start the read thread after we send the handshake ack since if we // fail to send the handshake ack, there is no reason to continue... @@ -142,7 +152,7 @@ GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) PacketResult packet_result = PacketResult::Success; const uint32_t timeout_usec = 10 * 1000; // Wait for 10 ms for a response while (packet_result == PacketResult::Success) - packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (response, timeout_usec); + packet_result = ReadPacket (response, timeout_usec, false); // The return value from QueryNoAckModeSupported() is true if the packet // was sent and _any_ response (including UNIMPLEMENTED) was received), @@ -150,11 +160,6 @@ GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) // a live connection to a remote GDB server... if (QueryNoAckModeSupported()) { -#if 0 - // Set above line to "#if 1" to test packet speed if remote GDB server - // supports the qSpeedTest packet... - TestPacketSpeed(10000); -#endif return true; } else @@ -172,13 +177,24 @@ GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) } bool +GDBRemoteCommunicationClient::GetEchoSupported () +{ + if (m_supports_qEcho == eLazyBoolCalculate) + { + GetRemoteQSupported(); + } + return m_supports_qEcho == eLazyBoolYes; +} + + +bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported () { if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } - return (m_supports_augmented_libraries_svr4_read == eLazyBoolYes); + return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; } bool @@ -188,7 +204,7 @@ GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported () { GetRemoteQSupported(); } - return (m_supports_qXfer_libraries_svr4_read == eLazyBoolYes); + return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; } bool @@ -198,7 +214,7 @@ GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported () { GetRemoteQSupported(); } - return (m_supports_qXfer_libraries_read == eLazyBoolYes); + return m_supports_qXfer_libraries_read == eLazyBoolYes; } bool @@ -208,7 +224,17 @@ GDBRemoteCommunicationClient::GetQXferAuxvReadSupported () { GetRemoteQSupported(); } - return (m_supports_qXfer_auxv_read == eLazyBoolYes); + return m_supports_qXfer_auxv_read == eLazyBoolYes; +} + +bool +GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported () +{ + if (m_supports_qXfer_features_read == eLazyBoolCalculate) + { + GetRemoteQSupported(); + } + return m_supports_qXfer_features_read == eLazyBoolYes; } uint64_t @@ -234,13 +260,10 @@ GDBRemoteCommunicationClient::QueryNoAckModeSupported () const uint32_t minimum_timeout = 6; uint32_t old_timeout = GetPacketTimeoutInMicroSeconds() / lldb_private::TimeValue::MicroSecPerSec; - SetPacketTimeout (std::max (old_timeout, minimum_timeout)); + GDBRemoteCommunication::ScopedTimeout timeout (*this, std::max (old_timeout, minimum_timeout)); StringExtractorGDBRemote response; - PacketResult packet_send_result = SendPacketAndWaitForResponse("QStartNoAckMode", response, false); - SetPacketTimeout (old_timeout); - - if (packet_send_result == PacketResult::Success) + if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { @@ -311,58 +334,64 @@ GDBRemoteCommunicationClient::GetSyncThreadStateSupported () void -GDBRemoteCommunicationClient::ResetDiscoverableSettings() -{ - m_supports_not_sending_acks = eLazyBoolCalculate; - m_supports_thread_suffix = eLazyBoolCalculate; - m_supports_threads_in_stop_reply = eLazyBoolCalculate; - m_supports_vCont_c = eLazyBoolCalculate; - m_supports_vCont_C = eLazyBoolCalculate; - m_supports_vCont_s = eLazyBoolCalculate; - m_supports_vCont_S = eLazyBoolCalculate; - m_supports_p = eLazyBoolCalculate; - m_supports_x = eLazyBoolCalculate; - m_supports_QSaveRegisterState = eLazyBoolCalculate; - m_qHostInfo_is_valid = eLazyBoolCalculate; - m_curr_pid_is_valid = eLazyBoolCalculate; +GDBRemoteCommunicationClient::ResetDiscoverableSettings (bool did_exec) +{ + if (did_exec == false) + { + // Hard reset everything, this is when we first connect to a GDB server + m_supports_not_sending_acks = eLazyBoolCalculate; + m_supports_thread_suffix = eLazyBoolCalculate; + m_supports_threads_in_stop_reply = eLazyBoolCalculate; + m_supports_vCont_c = eLazyBoolCalculate; + m_supports_vCont_C = eLazyBoolCalculate; + m_supports_vCont_s = eLazyBoolCalculate; + m_supports_vCont_S = eLazyBoolCalculate; + m_supports_p = eLazyBoolCalculate; + m_supports_x = eLazyBoolCalculate; + m_supports_QSaveRegisterState = eLazyBoolCalculate; + m_qHostInfo_is_valid = eLazyBoolCalculate; + m_curr_pid_is_valid = eLazyBoolCalculate; + m_qGDBServerVersion_is_valid = eLazyBoolCalculate; + m_supports_alloc_dealloc_memory = eLazyBoolCalculate; + m_supports_memory_region_info = eLazyBoolCalculate; + m_prepare_for_reg_writing_reply = eLazyBoolCalculate; + m_attach_or_wait_reply = eLazyBoolCalculate; + m_avoid_g_packets = eLazyBoolCalculate; + m_supports_qXfer_auxv_read = eLazyBoolCalculate; + m_supports_qXfer_libraries_read = eLazyBoolCalculate; + m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; + m_supports_qXfer_features_read = eLazyBoolCalculate; + m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; + m_supports_qProcessInfoPID = true; + m_supports_qfProcessInfo = true; + m_supports_qUserName = true; + m_supports_qGroupName = true; + m_supports_qThreadStopInfo = true; + m_supports_z0 = true; + m_supports_z1 = true; + m_supports_z2 = true; + m_supports_z3 = true; + m_supports_z4 = true; + m_supports_QEnvironment = true; + m_supports_QEnvironmentHexEncoded = true; + m_supports_qSymbol = true; + m_host_arch.Clear(); + m_os_version_major = UINT32_MAX; + m_os_version_minor = UINT32_MAX; + m_os_version_update = UINT32_MAX; + m_os_build.clear(); + m_os_kernel.clear(); + m_hostname.clear(); + m_gdb_server_name.clear(); + m_gdb_server_version = UINT32_MAX; + m_default_packet_timeout = 0; + m_max_packet_size = 0; + } + + // These flags should be reset when we first connect to a GDB server + // and when our inferior process execs m_qProcessInfo_is_valid = eLazyBoolCalculate; - m_qGDBServerVersion_is_valid = eLazyBoolCalculate; - m_supports_alloc_dealloc_memory = eLazyBoolCalculate; - m_supports_memory_region_info = eLazyBoolCalculate; - m_prepare_for_reg_writing_reply = eLazyBoolCalculate; - m_attach_or_wait_reply = eLazyBoolCalculate; - m_avoid_g_packets = eLazyBoolCalculate; - m_supports_qXfer_auxv_read = eLazyBoolCalculate; - m_supports_qXfer_libraries_read = eLazyBoolCalculate; - m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; - m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; - - m_supports_qProcessInfoPID = true; - m_supports_qfProcessInfo = true; - m_supports_qUserName = true; - m_supports_qGroupName = true; - m_supports_qThreadStopInfo = true; - m_supports_z0 = true; - m_supports_z1 = true; - m_supports_z2 = true; - m_supports_z3 = true; - m_supports_z4 = true; - m_supports_QEnvironment = true; - m_supports_QEnvironmentHexEncoded = true; - - m_host_arch.Clear(); m_process_arch.Clear(); - m_os_version_major = UINT32_MAX; - m_os_version_minor = UINT32_MAX; - m_os_version_update = UINT32_MAX; - m_os_build.clear(); - m_os_kernel.clear(); - m_hostname.clear(); - m_gdb_server_name.clear(); - m_gdb_server_version = UINT32_MAX; - m_default_packet_timeout = 0; - - m_max_packet_size = 0; } void @@ -373,10 +402,21 @@ GDBRemoteCommunicationClient::GetRemoteQSupported () m_supports_qXfer_libraries_read = eLazyBoolNo; m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo; + m_supports_qXfer_features_read = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if not, we assume no limit + // build the qSupported packet + std::vector<std::string> features = {"xmlRegisters=i386,arm,mips"}; + StreamString packet; + packet.PutCString( "qSupported" ); + for ( uint32_t i = 0; i < features.size( ); ++i ) + { + packet.PutCString( i==0 ? ":" : ";"); + packet.PutCString( features[i].c_str( ) ); + } + StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qSupported", + if (SendPacketAndWaitForResponse(packet.GetData(), response, /*send_async=*/false) == PacketResult::Success) { @@ -392,6 +432,66 @@ GDBRemoteCommunicationClient::GetRemoteQSupported () } if (::strstr (response_cstr, "qXfer:libraries:read+")) m_supports_qXfer_libraries_read = eLazyBoolYes; + if (::strstr (response_cstr, "qXfer:features:read+")) + m_supports_qXfer_features_read = eLazyBoolYes; + + + // Look for a list of compressions in the features list e.g. + // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma + const char *features_list = ::strstr (response_cstr, "qXfer:features:"); + if (features_list) + { + const char *compressions = ::strstr (features_list, "SupportedCompressions="); + if (compressions) + { + std::vector<std::string> supported_compressions; + compressions += sizeof ("SupportedCompressions=") - 1; + const char *end_of_compressions = strchr (compressions, ';'); + if (end_of_compressions == NULL) + { + end_of_compressions = strchr (compressions, '\0'); + } + const char *current_compression = compressions; + while (current_compression < end_of_compressions) + { + const char *next_compression_name = strchr (current_compression, ','); + const char *end_of_this_word = next_compression_name; + if (next_compression_name == NULL || end_of_compressions < next_compression_name) + { + end_of_this_word = end_of_compressions; + } + + if (end_of_this_word) + { + if (end_of_this_word == current_compression) + { + current_compression++; + } + else + { + std::string this_compression (current_compression, end_of_this_word - current_compression); + supported_compressions.push_back (this_compression); + current_compression = end_of_this_word + 1; + } + } + else + { + supported_compressions.push_back (current_compression); + current_compression = end_of_compressions; + } + } + + if (supported_compressions.size() > 0) + { + MaybeEnableCompression (supported_compressions); + } + } + } + + if (::strstr (response_cstr, "qEcho")) + m_supports_qEcho = eLazyBoolYes; + else + m_supports_qEcho = eLazyBoolNo; const char *packet_size_str = ::strstr (response_cstr, "PacketSize="); if (packet_size_str) @@ -509,6 +609,32 @@ GDBRemoteCommunicationClient::GetpPacketSupported (lldb::tid_t tid) return m_supports_p; } +StructuredData::ObjectSP +GDBRemoteCommunicationClient::GetThreadsInfo() +{ + // Get information on all threads at one using the "jThreadsInfo" packet + StructuredData::ObjectSP object_sp; + + if (m_supports_jThreadsInfo) + { + StringExtractorGDBRemote response; + m_supports_jThreadExtendedInfo = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success) + { + if (response.IsUnsupportedResponse()) + { + m_supports_jThreadsInfo = false; + } + else if (!response.Empty()) + { + object_sp = StructuredData::ParseJSON (response.GetStringRef()); + } + } + } + return object_sp; +} + + bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported () { @@ -619,7 +745,7 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponseNoLock (const char *pa { PacketResult packet_result = SendPacketNoLock (payload, payload_length); if (packet_result == PacketResult::Success) - packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ()); + packet_result = ReadPacket (response, GetPacketTimeoutInMicroSeconds (), true); return packet_result; } @@ -635,6 +761,12 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse PacketResult packet_result = PacketResult::ErrorSendFailed; Mutex::Locker locker; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + + // In order to stop async notifications from being processed in the middle of the + // send/recieve sequence Hijack the broadcast. Then rebroadcast any events when we are done. + static Listener hijack_listener("lldb.NotifyHijacker"); + HijackBroadcaster(&hijack_listener, eBroadcastBitGdbReadThreadGotNotify); + if (GetSequenceMutex (locker)) { packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response); @@ -726,6 +858,15 @@ GDBRemoteCommunicationClient::SendPacketAndWaitForResponse log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload); } } + + // Remove our Hijacking listner from the broadcast. + RestoreBroadcaster(); + + // If a notification event occured, rebroadcast since it can now be processed safely. + EventSP event_sp; + if (hijack_listener.GetNextEvent(event_sp)) + BroadcastEvent(event_sp); + return packet_result; } @@ -822,6 +963,57 @@ GDBRemoteCommunicationClient::HarmonizeThreadIdsForProfileData return final_output.str(); } +bool +GDBRemoteCommunicationClient::SendvContPacket +( + ProcessGDBRemote *process, + const char *payload, + size_t packet_length, + StringExtractorGDBRemote &response +) +{ + + m_curr_tid = LLDB_INVALID_THREAD_ID; + Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__); + + // we want to lock down packet sending while we continue + Mutex::Locker locker(m_sequence_mutex); + + // here we broadcast this before we even send the packet!! + // this signals doContinue() to exit + BroadcastEvent(eBroadcastBitRunPacketSent, NULL); + + // set the public state to running + m_public_is_running.SetValue(true, eBroadcastNever); + + // Set the starting continue packet into "continue_packet". This packet + // may change if we are interrupted and we continue after an async packet... + std::string continue_packet(payload, packet_length); + + if (log) + log->Printf("GDBRemoteCommunicationClient::%s () sending vCont packet: %s", __FUNCTION__, continue_packet.c_str()); + + if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success) + return false; + + // set the private state to running and broadcast this + m_private_is_running.SetValue(true, eBroadcastAlways); + + if (log) + log->Printf("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str()); + + // wait for the response to the vCont + if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success) + { + if (response.IsOKResponse()) + return true; + } + + return false; +} + StateType GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse ( @@ -844,7 +1036,10 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse // Set the starting continue packet into "continue_packet". This packet // may change if we are interrupted and we continue after an async packet... std::string continue_packet(payload, packet_length); - + + const auto sigstop_signo = process->GetUnixSignals().GetSignalNumberFromName("SIGSTOP"); + const auto sigint_signo = process->GetUnixSignals().GetSignalNumberFromName("SIGINT"); + bool got_async_packet = false; while (state == eStateRunning) @@ -864,9 +1059,9 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse got_async_packet = false; if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(%s)", __FUNCTION__, continue_packet.c_str()); + log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str()); - if (WaitForPacketWithTimeoutMicroSecondsNoLock(response, UINT32_MAX) == PacketResult::Success) + if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success) { if (response.Empty()) state = eStateInvalid; @@ -914,16 +1109,16 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse // packet. If we don't do this, then the reply for our // async packet will be the repeat stop reply packet and cause // a lot of trouble for us! - if (signo != SIGINT && signo != SIGSTOP) + if (signo != sigint_signo && signo != sigstop_signo) { continue_after_async = false; - // We didn't get a a SIGINT or SIGSTOP, so try for a + // We didn't get a SIGINT or SIGSTOP, so try for a // very brief time (1 ms) to get another stop reply // packet to make sure it doesn't get in the way StringExtractorGDBRemote extra_stop_reply_packet; uint32_t timeout_usec = 1000; - if (WaitForPacketWithTimeoutMicroSecondsNoLock (extra_stop_reply_packet, timeout_usec) == PacketResult::Success) + if (ReadPacket (extra_stop_reply_packet, timeout_usec, false) == PacketResult::Success) { switch (extra_stop_reply_packet.GetChar()) { @@ -1101,7 +1296,7 @@ GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse else { if (log) - log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(...) => false", __FUNCTION__); + log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(...) => false", __FUNCTION__); state = eStateInvalid; } } @@ -1287,7 +1482,7 @@ GDBRemoteCommunicationClient::SendArgumentsPacket (const ProcessLaunchInfo &laun const char *arg = NULL; const Args &launch_args = launch_info.GetArguments(); if (exe_file) - exe_path = exe_file.GetPath(); + exe_path = exe_file.GetPath(false); else { arg = launch_args.GetArgumentAtIndex(0); @@ -1576,6 +1771,105 @@ GDBRemoteCommunicationClient::GetGDBServerVersion() return m_qGDBServerVersion_is_valid == eLazyBoolYes; } +void +GDBRemoteCommunicationClient::MaybeEnableCompression (std::vector<std::string> supported_compressions) +{ + CompressionType avail_type = CompressionType::None; + std::string avail_name; + +#if defined (HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is available + if (compression_decode_buffer != NULL && avail_type == CompressionType::None) + { + for (auto compression : supported_compressions) + { + if (compression == "lzfse") + { + avail_type = CompressionType::LZFSE; + avail_name = compression; + break; + } + } + } +#endif + +#if defined (HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is available + if (compression_decode_buffer != NULL && avail_type == CompressionType::None) + { + for (auto compression : supported_compressions) + { + if (compression == "zlib-deflate") + { + avail_type = CompressionType::ZlibDeflate; + avail_name = compression; + break; + } + } + } +#endif + +#if defined (HAVE_LIBZ) + if (avail_type == CompressionType::None) + { + for (auto compression : supported_compressions) + { + if (compression == "zlib-deflate") + { + avail_type = CompressionType::ZlibDeflate; + avail_name = compression; + break; + } + } + } +#endif + +#if defined (HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is available + if (compression_decode_buffer != NULL && avail_type == CompressionType::None) + { + for (auto compression : supported_compressions) + { + if (compression == "lz4") + { + avail_type = CompressionType::LZ4; + avail_name = compression; + break; + } + } + } +#endif + +#if defined (HAVE_LIBCOMPRESSION) + // libcompression is weak linked so test if compression_decode_buffer() is available + if (compression_decode_buffer != NULL && avail_type == CompressionType::None) + { + for (auto compression : supported_compressions) + { + if (compression == "lzma") + { + avail_type = CompressionType::LZMA; + avail_name = compression; + break; + } + } + } +#endif + + if (avail_type != CompressionType::None) + { + StringExtractorGDBRemote response; + std::string packet = "QEnableCompression:type:" + avail_name + ";"; + if (SendPacketAndWaitForResponse (packet.c_str(), response, false) != PacketResult::Success) + return; + + if (response.IsOKResponse()) + { + m_compression_type = avail_type; + } + } +} + const char * GDBRemoteCommunicationClient::GetGDBServerProgramName() { @@ -1596,6 +1890,22 @@ GDBRemoteCommunicationClient::GetGDBServerProgramVersion() } bool +GDBRemoteCommunicationClient::GetDefaultThreadId (lldb::tid_t &tid) +{ + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC",response,false) != PacketResult::Success) + return false; + + if (!response.IsNormalResponse()) + return false; + + if (response.GetChar() == 'Q' && response.GetChar() == 'C') + tid = response.GetHexMaxU32(true, -1); + + return true; +} + +bool GDBRemoteCommunicationClient::GetHostInfo (bool force) { Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS)); @@ -2167,13 +2477,14 @@ GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after } int -GDBRemoteCommunicationClient::SetSTDIN (char const *path) +GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { - if (path && path[0]) + if (file_spec) { + std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDIN:"); - packet.PutBytesAsRawHex8(path, strlen(path)); + packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) @@ -2189,14 +2500,15 @@ GDBRemoteCommunicationClient::SetSTDIN (char const *path) } int -GDBRemoteCommunicationClient::SetSTDOUT (char const *path) +GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { - if (path && path[0]) + if (file_spec) { + std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDOUT:"); - packet.PutBytesAsRawHex8(path, strlen(path)); - + packet.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { @@ -2211,14 +2523,15 @@ GDBRemoteCommunicationClient::SetSTDOUT (char const *path) } int -GDBRemoteCommunicationClient::SetSTDERR (char const *path) +GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { - if (path && path[0]) + if (file_spec) { + std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDERR:"); - packet.PutBytesAsRawHex8(path, strlen(path)); - + packet.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { @@ -2233,7 +2546,7 @@ GDBRemoteCommunicationClient::SetSTDERR (char const *path) } bool -GDBRemoteCommunicationClient::GetWorkingDir (std::string &cwd) +GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse ("qGetWorkingDir", response, false) == PacketResult::Success) @@ -2242,21 +2555,24 @@ GDBRemoteCommunicationClient::GetWorkingDir (std::string &cwd) return false; if (response.IsErrorResponse()) return false; - response.GetHexByteString (cwd); + std::string cwd; + response.GetHexByteString(cwd); + working_dir.SetFile(cwd, false, GetHostArchitecture()); return !cwd.empty(); } return false; } int -GDBRemoteCommunicationClient::SetWorkingDir (char const *path) +GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { - if (path && path[0]) + if (working_dir) { + std::string path{working_dir.GetPath(false)}; StreamString packet; packet.PutCString("QSetWorkingDir:"); - packet.PutBytesAsRawHex8(path, strlen(path)); - + packet.PutCStringAsRawHex8(path.c_str()); + StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { @@ -2653,6 +2969,9 @@ GDBRemoteCommunicationClient::FindProcesses (const ProcessInstanceInfoMatch &mat } } StringExtractorGDBRemote response; + // Increase timeout as the first qfProcessInfo packet takes a long time + // on Android. The value of 1min was arrived at empirically. + GDBRemoteCommunication::ScopedTimeout timeout (*this, 60); if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { do @@ -2734,87 +3053,184 @@ GDBRemoteCommunicationClient::GetGroupName (uint32_t gid, std::string &name) return false; } +bool +GDBRemoteCommunicationClient::SetNonStopMode (const bool enable) +{ + // Form non-stop packet request + char packet[32]; + const int packet_len = ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); + assert(packet_len < (int)sizeof(packet)); + + StringExtractorGDBRemote response; + // Send to target + if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) + if (response.IsOKResponse()) + return true; + + // Failed or not supported + return false; + +} + +static void +MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, uint32_t recv_size) +{ + packet.Clear(); + packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); + uint32_t bytes_left = send_size; + while (bytes_left > 0) + { + if (bytes_left >= 26) + { + packet.PutCString("abcdefghijklmnopqrstuvwxyz"); + bytes_left -= 26; + } + else + { + packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); + bytes_left = 0; + } + } +} + +template<typename T> +T calculate_standard_deviation(const std::vector<T> &v) +{ + T sum = std::accumulate(std::begin(v), std::end(v), T(0)); + T mean = sum / (T)v.size(); + T accum = T(0); + std::for_each (std::begin(v), std::end(v), [&](const T d) { + T delta = d - mean; + accum += delta * delta; + }); + + T stdev = sqrt(accum / (v.size()-1)); + return stdev; +} + void -GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets) +GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, bool json, Stream &strm) { uint32_t i; TimeValue start_time, end_time; uint64_t total_time_nsec; if (SendSpeedTestPacket (0, 0)) { - static uint32_t g_send_sizes[] = { 0, 64, 128, 512, 1024 }; - static uint32_t g_recv_sizes[] = { 0, 64, 128, 512, 1024 }; //, 4*1024, 8*1024, 16*1024, 32*1024, 48*1024, 64*1024, 96*1024, 128*1024 }; - const size_t k_num_send_sizes = llvm::array_lengthof(g_send_sizes); - const size_t k_num_recv_sizes = llvm::array_lengthof(g_recv_sizes); - const uint64_t k_recv_amount = 4*1024*1024; // Receive 4MB - for (uint32_t send_idx = 0; send_idx < k_num_send_sizes; ++send_idx) - { - const uint32_t send_size = g_send_sizes[send_idx]; - for (uint32_t recv_idx = 0; recv_idx < k_num_recv_sizes; ++recv_idx) - { - const uint32_t recv_size = g_recv_sizes[recv_idx]; - StreamString packet; - packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); - uint32_t bytes_left = send_size; - while (bytes_left > 0) + StreamString packet; + if (json) + strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n \"results\" : [", num_packets); + else + strm.Printf("Testing sending %u packets of various sizes:\n", num_packets); + strm.Flush(); + + uint32_t result_idx = 0; + uint32_t send_size; + std::vector<float> packet_times; + + for (send_size = 0; send_size <= max_send; send_size ? send_size *= 2 : send_size = 4) + { + for (uint32_t recv_size = 0; recv_size <= max_recv; recv_size ? recv_size *= 2 : recv_size = 4) + { + MakeSpeedTestPacket (packet, send_size, recv_size); + + packet_times.clear(); + // Test how long it takes to send 'num_packets' packets + start_time = TimeValue::Now(); + for (i=0; i<num_packets; ++i) { - if (bytes_left >= 26) - { - packet.PutCString("abcdefghijklmnopqrstuvwxyz"); - bytes_left -= 26; - } - else - { - packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); - bytes_left = 0; - } + TimeValue packet_start_time = TimeValue::Now(); + StringExtractorGDBRemote response; + SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); + TimeValue packet_end_time = TimeValue::Now(); + uint64_t packet_time_nsec = packet_end_time.GetAsNanoSecondsSinceJan1_1970() - packet_start_time.GetAsNanoSecondsSinceJan1_1970(); + packet_times.push_back((float)packet_time_nsec); } + end_time = TimeValue::Now(); + total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); - start_time = TimeValue::Now(); - if (recv_size == 0) + float packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; + float total_ms = (float)total_time_nsec/(float)TimeValue::NanoSecPerMilliSec; + float average_ms_per_packet = total_ms / num_packets; + const float standard_deviation = calculate_standard_deviation<float>(packet_times); + if (json) { - for (i=0; i<num_packets; ++i) - { - StringExtractorGDBRemote response; - SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); - } + strm.Printf ("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 ", \"standard_deviation_nsec\" : %9" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, total_time_nsec, (uint64_t)standard_deviation); + ++result_idx; } else { - uint32_t bytes_read = 0; - while (bytes_read < k_recv_amount) - { - StringExtractorGDBRemote response; - SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); - bytes_read += recv_size; - } + strm.Printf ("qSpeedTest(send=%-7u, recv=%-7u) in %" PRIu64 ".%9.9" PRIu64 " sec for %9.2f packets/sec (%10.6f ms per packet) with standard deviation of %10.6f ms\n", + send_size, + recv_size, + total_time_nsec / TimeValue::NanoSecPerSec, + total_time_nsec % TimeValue::NanoSecPerSec, + packets_per_second, + average_ms_per_packet, + standard_deviation/(float)TimeValue::NanoSecPerMilliSec); + } + strm.Flush(); + } + } + + const uint64_t k_recv_amount = 4*1024*1024; // Receive amount in bytes + + const float k_recv_amount_mb = (float)k_recv_amount/(1024.0f*1024.0f); + if (json) + strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" : %" PRIu64 ",\n \"results\" : [", k_recv_amount); + else + strm.Printf("Testing receiving %2.1fMB of data using varying receive packet sizes:\n", k_recv_amount_mb); + strm.Flush(); + send_size = 0; + result_idx = 0; + for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) + { + MakeSpeedTestPacket (packet, send_size, recv_size); + + // If we have a receive size, test how long it takes to receive 4MB of data + if (recv_size > 0) + { + start_time = TimeValue::Now(); + uint32_t bytes_read = 0; + uint32_t packet_count = 0; + while (bytes_read < k_recv_amount) + { + StringExtractorGDBRemote response; + SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); + bytes_read += recv_size; + ++packet_count; } end_time = TimeValue::Now(); total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); - if (recv_size == 0) + float mb_second = ((((float)k_recv_amount)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec) / (1024.0*1024.0); + float packets_per_second = (((float)packet_count)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; + float total_ms = (float)total_time_nsec/(float)TimeValue::NanoSecPerMilliSec; + float average_ms_per_packet = total_ms / packet_count; + + if (json) { - float packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; - printf ("%u qSpeedTest(send=%-7u, recv=%-7u) in %" PRIu64 ".%9.9" PRIu64 " sec for %f packets/sec.\n", - num_packets, - send_size, - recv_size, - total_time_nsec / TimeValue::NanoSecPerSec, - total_time_nsec % TimeValue::NanoSecPerSec, - packets_per_second); + strm.Printf ("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, total_time_nsec); + ++result_idx; } else { - float mb_second = ((((float)k_recv_amount)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec) / (1024.0*1024.0); - printf ("%u qSpeedTest(send=%-7u, recv=%-7u) sent 4MB in %" PRIu64 ".%9.9" PRIu64 " sec for %f MB/sec.\n", - num_packets, - send_size, - recv_size, - total_time_nsec / TimeValue::NanoSecPerSec, - total_time_nsec % TimeValue::NanoSecPerSec, - mb_second); + strm.Printf ("qSpeedTest(send=%-7u, recv=%-7u) %6u packets needed to receive %2.1fMB in %" PRIu64 ".%9.9" PRIu64 " sec for %f MB/sec for %9.2f packets/sec (%10.6f ms per packet)\n", + send_size, + recv_size, + packet_count, + k_recv_amount_mb, + total_time_nsec / TimeValue::NanoSecPerSec, + total_time_nsec % TimeValue::NanoSecPerSec, + mb_second, + packets_per_second, + average_ms_per_packet); } + strm.Flush(); } } + if (json) + strm.Printf("\n ]\n }\n}\n"); + else + strm.EOL(); } } @@ -2869,10 +3285,9 @@ GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort (lldb::pid_t &pid, const int packet_len = stream.GetSize(); // give the process a few seconds to startup - const uint32_t old_packet_timeout = SetPacketTimeout (10); - auto result = SendPacketAndWaitForResponse(packet, packet_len, response, false); - SetPacketTimeout (old_packet_timeout); - if (result == PacketResult::Success) + GDBRemoteCommunication::ScopedTimeout timeout (*this, 10); + + if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { std::string name; std::string value; @@ -3109,22 +3524,23 @@ GDBRemoteCommunicationClient::GetShlibInfoAddr() } lldb_private::Error -GDBRemoteCommunicationClient::RunShellCommand (const char *command, // Shouldn't be NULL - const char *working_dir, // Pass NULL to use the current working directory - int *status_ptr, // Pass NULL if you don't want the process exit status - int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit - std::string *command_output, // Pass NULL if you don't want the command output - uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish +GDBRemoteCommunicationClient::RunShellCommand(const char *command, // Shouldn't be NULL + const FileSpec &working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); stream.PutBytesAsRawHex8(command, strlen(command)); stream.PutChar(','); stream.PutHex32(timeout_sec); - if (working_dir && *working_dir) + if (working_dir) { + std::string path{working_dir.GetPath(false)}; stream.PutChar(','); - stream.PutBytesAsRawHex8(working_dir, strlen(working_dir)); + stream.PutCStringAsRawHex8(path.c_str()); } const char *packet = stream.GetData(); int packet_len = stream.GetSize(); @@ -3157,43 +3573,49 @@ GDBRemoteCommunicationClient::RunShellCommand (const char *command, // } Error -GDBRemoteCommunicationClient::MakeDirectory (const char *path, - uint32_t file_permissions) +GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, + uint32_t file_permissions) { + std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_mkdir:"); stream.PutHex32(file_permissions); stream.PutChar(','); - stream.PutBytesAsRawHex8(path, strlen(path)); + stream.PutCStringAsRawHex8(path.c_str()); const char *packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - return Error(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); - } - return Error(); + if (SendPacketAndWaitForResponse(packet, packet_len, response, false) != PacketResult::Success) + return Error("failed to send '%s' packet", packet); + + if (response.GetChar() != 'F') + return Error("invalid response to '%s' packet", packet); + + return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } Error -GDBRemoteCommunicationClient::SetFilePermissions (const char *path, - uint32_t file_permissions) +GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions) { + std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_chmod:"); stream.PutHex32(file_permissions); stream.PutChar(','); - stream.PutBytesAsRawHex8(path, strlen(path)); + stream.PutCStringAsRawHex8(path.c_str()); const char *packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) - { - return Error(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); - } - return Error(); - + + if (SendPacketAndWaitForResponse(packet, packet_len, response, false) != PacketResult::Success) + return Error("failed to send '%s' packet", packet); + + if (response.GetChar() != 'F') + return Error("invalid response to '%s' packet", packet); + + return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } static uint64_t @@ -3225,15 +3647,14 @@ GDBRemoteCommunicationClient::OpenFile (const lldb_private::FileSpec& file_spec, mode_t mode, Error &error) { + std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:open:"); - std::string path (file_spec.GetPath()); if (path.empty()) return UINT64_MAX; stream.PutCStringAsRawHex8(path.c_str()); stream.PutChar(','); - const uint32_t posix_open_flags = File::ConvertOpenOptionsForPOSIXOpen(flags); - stream.PutHex32(posix_open_flags); + stream.PutHex32(flags); stream.PutChar(','); stream.PutHex32(mode); const char* packet = stream.GetData(); @@ -3266,9 +3687,9 @@ GDBRemoteCommunicationClient::CloseFile (lldb::user_id_t fd, lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_spec) { + std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:size:"); - std::string path (file_spec.GetPath()); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); @@ -3284,12 +3705,14 @@ GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_sp } Error -GDBRemoteCommunicationClient::GetFilePermissions(const char *path, uint32_t &file_permissions) +GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions) { + std::string path{file_spec.GetPath(false)}; Error error; lldb_private::StreamString stream; stream.PutCString("vFile:mode:"); - stream.PutCStringAsRawHex8(path); + stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; @@ -3408,16 +3831,18 @@ GDBRemoteCommunicationClient::WriteFile (lldb::user_id_t fd, } Error -GDBRemoteCommunicationClient::CreateSymlink (const char *src, const char *dst) +GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, const FileSpec &dst) { + std::string src_path{src.GetPath(false)}, + dst_path{dst.GetPath(false)}; Error error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:symlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here - stream.PutCStringAsRawHex8(dst); + stream.PutCStringAsRawHex8(dst_path.c_str()); stream.PutChar(','); - stream.PutCStringAsRawHex8(src); + stream.PutCStringAsRawHex8(src_path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; @@ -3451,14 +3876,15 @@ GDBRemoteCommunicationClient::CreateSymlink (const char *src, const char *dst) } Error -GDBRemoteCommunicationClient::Unlink (const char *path) +GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { + std::string path{file_spec.GetPath(false)}; Error error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:unlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here - stream.PutCStringAsRawHex8(path); + stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; @@ -3495,9 +3921,9 @@ GDBRemoteCommunicationClient::Unlink (const char *path) bool GDBRemoteCommunicationClient::GetFileExists (const lldb_private::FileSpec& file_spec) { + std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:exists:"); - std::string path (file_spec.GetPath()); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); @@ -3519,9 +3945,9 @@ GDBRemoteCommunicationClient::CalculateMD5 (const lldb_private::FileSpec& file_s uint64_t &high, uint64_t &low) { + std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:MD5:"); - std::string path (file_spec.GetPath()); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); @@ -3639,7 +4065,7 @@ GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save if (thread_suffix_supported) ::snprintf (packet, sizeof(packet), "QSaveRegisterState;thread:%4.4" PRIx64 ";", tid); else - ::strncpy (packet, "QSaveRegisterState", sizeof(packet)); + ::snprintf(packet, sizeof(packet), "QSaveRegisterState"); StringExtractorGDBRemote response; @@ -3703,3 +4129,274 @@ GDBRemoteCommunicationClient::RestoreRegisterState (lldb::tid_t tid, uint32_t sa } return false; } + +bool +GDBRemoteCommunicationClient::GetModuleInfo (const FileSpec& module_file_spec, + const lldb_private::ArchSpec& arch_spec, + ModuleSpec &module_spec) +{ + std::string module_path = module_file_spec.GetPath (false); + if (module_path.empty ()) + return false; + + StreamString packet; + packet.PutCString("qModuleInfo:"); + packet.PutCStringAsRawHex8(module_path.c_str()); + packet.PutCString(";"); + const auto& triple = arch_spec.GetTriple().getTriple(); + packet.PutCStringAsRawHex8(triple.c_str()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) != PacketResult::Success) + return false; + + if (response.IsErrorResponse () || response.IsUnsupportedResponse ()) + return false; + + std::string name; + std::string value; + bool success; + StringExtractor extractor; + + module_spec.Clear (); + module_spec.GetFileSpec () = module_file_spec; + + while (response.GetNameColonValue (name, value)) + { + if (name == "uuid" || name == "md5") + { + extractor.GetStringRef ().swap (value); + extractor.SetFilePos (0); + extractor.GetHexByteString (value); + module_spec.GetUUID().SetFromCString (value.c_str(), value.size() / 2); + } + else if (name == "triple") + { + extractor.GetStringRef ().swap (value); + extractor.SetFilePos (0); + extractor.GetHexByteString (value); + module_spec.GetArchitecture().SetTriple (value.c_str ()); + } + else if (name == "file_offset") + { + const auto ival = StringConvert::ToUInt64 (value.c_str (), 0, 16, &success); + if (success) + module_spec.SetObjectOffset (ival); + } + else if (name == "file_size") + { + const auto ival = StringConvert::ToUInt64 (value.c_str (), 0, 16, &success); + if (success) + module_spec.SetObjectSize (ival); + } + else if (name == "file_path") + { + extractor.GetStringRef ().swap (value); + extractor.SetFilePos (0); + extractor.GetHexByteString (value); + module_spec.GetFileSpec() = FileSpec(value.c_str(), false, arch_spec); + } + } + + return true; +} + +// query the target remote for extended information using the qXfer packet +// +// example: object='features', annex='target.xml', out=<xml output> +// return: 'true' on success +// 'false' on failure (err set) +bool +GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString object, + const lldb_private::ConstString annex, + std::string & out, + lldb_private::Error & err) { + + std::stringstream output; + StringExtractorGDBRemote chunk; + + uint64_t size = GetRemoteMaxPacketSize(); + if (size == 0) + size = 0x1000; + size = size - 1; // Leave space for the 'm' or 'l' character in the response + int offset = 0; + bool active = true; + + // loop until all data has been read + while ( active ) { + + // send query extended feature packet + std::stringstream packet; + packet << "qXfer:" + << object.AsCString("") << ":read:" + << annex.AsCString("") << ":" + << std::hex << offset << "," + << std::hex << size; + + GDBRemoteCommunication::PacketResult res = + SendPacketAndWaitForResponse( packet.str().c_str(), + chunk, + false ); + + if ( res != GDBRemoteCommunication::PacketResult::Success ) { + err.SetErrorString( "Error sending $qXfer packet" ); + return false; + } + + const std::string & str = chunk.GetStringRef( ); + if ( str.length() == 0 ) { + // should have some data in chunk + err.SetErrorString( "Empty response from $qXfer packet" ); + return false; + } + + // check packet code + switch ( str[0] ) { + // last chunk + case ( 'l' ): + active = false; + // fall through intentional + + // more chunks + case ( 'm' ) : + if ( str.length() > 1 ) + output << &str[1]; + offset += size; + break; + + // unknown chunk + default: + err.SetErrorString( "Invalid continuation code from $qXfer packet" ); + return false; + } + } + + out = output.str( ); + err.Success( ); + return true; +} + +// Notify the target that gdb is prepared to serve symbol lookup requests. +// packet: "qSymbol::" +// reply: +// OK The target does not need to look up any (more) symbols. +// qSymbol:<sym_name> The target requests the value of symbol sym_name (hex encoded). +// LLDB may provide the value by sending another qSymbol packet +// in the form of"qSymbol:<sym_value>:<sym_name>". + +void +GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process) +{ + if (m_supports_qSymbol) + { + Mutex::Locker locker; + if (GetSequenceMutex(locker, "GDBRemoteCommunicationClient::ServeSymbolLookups() failed due to not getting the sequence mutex")) + { + StreamString packet; + packet.PutCString ("qSymbol::"); + while (1) + { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponseNoLock(packet.GetData(), packet.GetSize(), response) == PacketResult::Success) + { + if (response.IsOKResponse()) + { + // We are done serving symbols requests + return; + } + + if (response.IsUnsupportedResponse()) + { + // qSymbol is not supported by the current GDB server we are connected to + m_supports_qSymbol = false; + return; + } + else + { + llvm::StringRef response_str(response.GetStringRef()); + if (response_str.startswith("qSymbol:")) + { + response.SetFilePos(strlen("qSymbol:")); + std::string symbol_name; + if (response.GetHexByteString(symbol_name)) + { + if (symbol_name.empty()) + return; + + addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; + lldb_private::SymbolContextList sc_list; + if (process->GetTarget().GetImages().FindSymbolsWithNameAndType(ConstString(symbol_name), eSymbolTypeAny, sc_list)) + { + const size_t num_scs = sc_list.GetSize(); + for (size_t sc_idx=0; sc_idx<num_scs && symbol_load_addr == LLDB_INVALID_ADDRESS; ++sc_idx) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(sc_idx, sc)) + { + if (sc.symbol) + { + switch (sc.symbol->GetType()) + { + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeUndefined: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeVariable: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeTrampoline: + break; + + case eSymbolTypeCode: + case eSymbolTypeResolver: + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeException: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + case eSymbolTypeReExported: + symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); + break; + } + } + } + } + } + // This is the normal path where our symbol lookup was successful and we want + // to send a packet with the new symbol value and see if another lookup needs to be + // done. + + // Change "packet" to contain the requested symbol value and name + packet.Clear(); + packet.PutCString("qSymbol:"); + if (symbol_load_addr != LLDB_INVALID_ADDRESS) + packet.Printf("%" PRIx64, symbol_load_addr); + packet.PutCString(":"); + packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); + continue; // go back to the while loop and send "packet" and wait for another response + } + } + } + } + } + // If we make it here, the symbol request packet response wasn't valid or + // our symbol lookup failed so we must abort + return; + + } + } +} + diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index d90614bce88b..65a2981018ea 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -17,17 +17,21 @@ // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" +#include "lldb/Core/StructuredData.h" #include "lldb/Target/Process.h" #include "GDBRemoteCommunication.h" +namespace lldb_private { +namespace process_gdb_remote { + class GDBRemoteCommunicationClient : public GDBRemoteCommunication { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ - GDBRemoteCommunicationClient(bool is_platform); + GDBRemoteCommunicationClient(); ~GDBRemoteCommunicationClient(); @@ -36,7 +40,7 @@ public: // we are communicating with it. //------------------------------------------------------------------ bool - HandshakeWithServer (lldb_private::Error *error_ptr); + HandshakeWithServer (Error *error_ptr); PacketResult SendPacketAndWaitForResponse (const char *send_payload, @@ -75,9 +79,14 @@ public: const char *packet_payload, size_t packet_length, StringExtractorGDBRemote &response); + bool + SendvContPacket (ProcessGDBRemote *process, + const char *payload, + size_t packet_length, + StringExtractorGDBRemote &response); bool - GetThreadSuffixSupported (); + GetThreadSuffixSupported () override; // This packet is usually sent first and the boolean return value // indicates if the packet was send and any response was received @@ -95,7 +104,7 @@ public: SendAsyncSignal (int signo); bool - SendInterrupt (lldb_private::Mutex::Locker &locker, + SendInterrupt (Mutex::Locker &locker, uint32_t seconds_to_wait_for_stop, bool &timed_out); @@ -126,7 +135,7 @@ public: /// response was received. //------------------------------------------------------------------ int - SendArgumentsPacket (const lldb_private::ProcessLaunchInfo &launch_info); + SendArgumentsPacket (const ProcessLaunchInfo &launch_info); //------------------------------------------------------------------ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the @@ -202,11 +211,11 @@ public: /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int - SetSTDIN (char const *path); + SetSTDIN(const FileSpec &file_spec); int - SetSTDOUT (char const *path); + SetSTDOUT(const FileSpec &file_spec); int - SetSTDERR (char const *path); + SetSTDERR(const FileSpec &file_spec); //------------------------------------------------------------------ /// Sets the disable ASLR flag to \a enable for a process that will @@ -240,27 +249,27 @@ public: /// implements the platform, it will change the current working /// directory for the platform process. /// - /// @param[in] path + /// @param[in] working_dir /// The path to a directory to use when launching our process /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int - SetWorkingDir (char const *path); + SetWorkingDir(const FileSpec &working_dir); //------------------------------------------------------------------ /// Gets the current working directory of a remote platform GDB /// server. /// - /// @param[out] cwd + /// @param[out] working_dir /// The current working directory on the remote platform. /// /// @return /// Boolean for success //------------------------------------------------------------------ bool - GetWorkingDir (std::string &cwd); + GetWorkingDir(FileSpec &working_dir); lldb::addr_t AllocateMemory (size_t size, uint32_t permissions); @@ -268,29 +277,28 @@ public: bool DeallocateMemory (lldb::addr_t addr); - lldb_private::Error + Error Detach (bool keep_stopped); - lldb_private::Error - GetMemoryRegionInfo (lldb::addr_t addr, - lldb_private::MemoryRegionInfo &range_info); + Error + GetMemoryRegionInfo (lldb::addr_t addr, MemoryRegionInfo &range_info); - lldb_private::Error + Error GetWatchpointSupportInfo (uint32_t &num); - lldb_private::Error + Error GetWatchpointSupportInfo (uint32_t &num, bool& after); - lldb_private::Error + Error GetWatchpointsTriggerAfterInstruction (bool &after); - const lldb_private::ArchSpec & + const ArchSpec & GetHostArchitecture (); uint32_t GetHostDefaultPacketTimeout(); - const lldb_private::ArchSpec & + const ArchSpec & GetProcessArchitecture (); void @@ -312,10 +320,13 @@ public: GetSyncThreadStateSupported(); void - ResetDiscoverableSettings(); + ResetDiscoverableSettings (bool did_exec); bool GetHostInfo (bool force = false); + + bool + GetDefaultThreadId (lldb::tid_t &tid); bool GetOSVersion (uint32_t &major, @@ -328,7 +339,7 @@ public: bool GetOSKernelDescription (std::string &s); - lldb_private::ArchSpec + ArchSpec GetSystemArchitecture (); bool @@ -341,12 +352,11 @@ public: GetSupportsThreadSuffix (); bool - GetProcessInfo (lldb::pid_t pid, - lldb_private::ProcessInstanceInfo &process_info); + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info); uint32_t - FindProcesses (const lldb_private::ProcessInstanceInfoMatch &process_match_info, - lldb_private::ProcessInstanceInfoList &process_infos); + FindProcesses (const ProcessInstanceInfoMatch &process_match_info, + ProcessInstanceInfoList &process_infos); bool GetUserName (uint32_t uid, std::string &name); @@ -383,7 +393,7 @@ public: case eWatchpointWrite: return m_supports_z2; case eWatchpointRead: return m_supports_z3; case eWatchpointReadWrite: return m_supports_z4; - case eStoppointInvalid: return false; + default: return false; } } uint8_t @@ -392,8 +402,11 @@ public: lldb::addr_t addr, // Address of breakpoint or watchpoint uint32_t length); // Byte Size of breakpoint or watchpoint + bool + SetNonStopMode (const bool enable); + void - TestPacketSpeed (const uint32_t num_packets); + TestPacketSpeed (const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, bool json, Stream &strm); // This packet is for testing the speed of the interface only. Both // the client and server need to support it, but this allows us to @@ -423,9 +436,15 @@ public: GetRemoteMaxPacketSize(); bool + GetEchoSupported (); + + bool GetAugmentedLibrariesSVR4ReadSupported (); - lldb_private::LazyBool + bool + GetQXferFeaturesReadSupported (); + + LazyBool SupportsAllocDeallocMemory () // const { // Uncomment this to have lldb pretend the debug server doesn't respond to alloc/dealloc memory packets. @@ -444,64 +463,57 @@ public: } lldb::user_id_t - OpenFile (const lldb_private::FileSpec& file_spec, - uint32_t flags, - mode_t mode, - lldb_private::Error &error); + OpenFile (const FileSpec& file_spec, uint32_t flags, mode_t mode, Error &error); bool - CloseFile (lldb::user_id_t fd, - lldb_private::Error &error); + CloseFile (lldb::user_id_t fd, Error &error); lldb::user_id_t - GetFileSize (const lldb_private::FileSpec& file_spec); + GetFileSize (const FileSpec& file_spec); - lldb_private::Error - GetFilePermissions(const char *path, uint32_t &file_permissions); + Error + GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions); - lldb_private::Error - SetFilePermissions(const char *path, uint32_t file_permissions); + Error + SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions); uint64_t ReadFile (lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, - lldb_private::Error &error); + Error &error); uint64_t WriteFile (lldb::user_id_t fd, uint64_t offset, const void* src, uint64_t src_len, - lldb_private::Error &error); + Error &error); - lldb_private::Error - CreateSymlink (const char *src, - const char *dst); + Error + CreateSymlink(const FileSpec &src, + const FileSpec &dst); - lldb_private::Error - Unlink (const char *path); + Error + Unlink(const FileSpec &file_spec); + + Error + MakeDirectory(const FileSpec &file_spec, uint32_t mode); - lldb_private::Error - MakeDirectory (const char *path, - uint32_t mode); - bool - GetFileExists (const lldb_private::FileSpec& file_spec); - - lldb_private::Error - RunShellCommand (const char *command, // Shouldn't be NULL - const char *working_dir, // Pass NULL to use the current working directory - int *status_ptr, // Pass NULL if you don't want the process exit status - int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit - std::string *command_output, // Pass NULL if you don't want the command output - uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + GetFileExists (const FileSpec& file_spec); + Error + RunShellCommand(const char *command, // Shouldn't be NULL + const FileSpec &working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + bool - CalculateMD5 (const lldb_private::FileSpec& file_spec, - uint64_t &high, - uint64_t &low); + CalculateMD5 (const FileSpec& file_spec, uint64_t &high, uint64_t &low); std::string HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process, @@ -531,9 +543,26 @@ public: bool AvoidGPackets(ProcessGDBRemote *process); + StructuredData::ObjectSP + GetThreadsInfo(); + bool GetThreadExtendedInfoSupported(); + bool + GetModuleInfo (const FileSpec& module_file_spec, + const ArchSpec& arch_spec, + ModuleSpec &module_spec); + + bool + ReadExtFeature (const lldb_private::ConstString object, + const lldb_private::ConstString annex, + std::string & out, + lldb_private::Error & err); + + void + ServeSymbolLookups(lldb_private::Process *process); + protected: PacketResult @@ -547,38 +576,44 @@ protected: bool GetGDBServerVersion(); + // Given the list of compression types that the remote debug stub can support, + // possibly enable compression if we find an encoding we can handle. + void + MaybeEnableCompression (std::vector<std::string> supported_compressions); + //------------------------------------------------------------------ // Classes that inherit from GDBRemoteCommunicationClient can see and modify these //------------------------------------------------------------------ - lldb_private::LazyBool m_supports_not_sending_acks; - lldb_private::LazyBool m_supports_thread_suffix; - lldb_private::LazyBool m_supports_threads_in_stop_reply; - lldb_private::LazyBool m_supports_vCont_all; - lldb_private::LazyBool m_supports_vCont_any; - lldb_private::LazyBool m_supports_vCont_c; - lldb_private::LazyBool m_supports_vCont_C; - lldb_private::LazyBool m_supports_vCont_s; - lldb_private::LazyBool m_supports_vCont_S; - lldb_private::LazyBool m_qHostInfo_is_valid; - lldb_private::LazyBool m_curr_pid_is_valid; - lldb_private::LazyBool m_qProcessInfo_is_valid; - lldb_private::LazyBool m_qGDBServerVersion_is_valid; - lldb_private::LazyBool m_supports_alloc_dealloc_memory; - lldb_private::LazyBool m_supports_memory_region_info; - lldb_private::LazyBool m_supports_watchpoint_support_info; - lldb_private::LazyBool m_supports_detach_stay_stopped; - lldb_private::LazyBool m_watchpoints_trigger_after_instruction; - lldb_private::LazyBool m_attach_or_wait_reply; - lldb_private::LazyBool m_prepare_for_reg_writing_reply; - lldb_private::LazyBool m_supports_p; - lldb_private::LazyBool m_supports_x; - lldb_private::LazyBool m_avoid_g_packets; - lldb_private::LazyBool m_supports_QSaveRegisterState; - lldb_private::LazyBool m_supports_qXfer_auxv_read; - lldb_private::LazyBool m_supports_qXfer_libraries_read; - lldb_private::LazyBool m_supports_qXfer_libraries_svr4_read; - lldb_private::LazyBool m_supports_augmented_libraries_svr4_read; - lldb_private::LazyBool m_supports_jThreadExtendedInfo; + LazyBool m_supports_not_sending_acks; + LazyBool m_supports_thread_suffix; + LazyBool m_supports_threads_in_stop_reply; + LazyBool m_supports_vCont_all; + LazyBool m_supports_vCont_any; + LazyBool m_supports_vCont_c; + LazyBool m_supports_vCont_C; + LazyBool m_supports_vCont_s; + LazyBool m_supports_vCont_S; + LazyBool m_qHostInfo_is_valid; + LazyBool m_curr_pid_is_valid; + LazyBool m_qProcessInfo_is_valid; + LazyBool m_qGDBServerVersion_is_valid; + LazyBool m_supports_alloc_dealloc_memory; + LazyBool m_supports_memory_region_info; + LazyBool m_supports_watchpoint_support_info; + LazyBool m_supports_detach_stay_stopped; + LazyBool m_watchpoints_trigger_after_instruction; + LazyBool m_attach_or_wait_reply; + LazyBool m_prepare_for_reg_writing_reply; + LazyBool m_supports_p; + LazyBool m_supports_x; + LazyBool m_avoid_g_packets; + LazyBool m_supports_QSaveRegisterState; + LazyBool m_supports_qXfer_auxv_read; + LazyBool m_supports_qXfer_libraries_read; + LazyBool m_supports_qXfer_libraries_svr4_read; + LazyBool m_supports_qXfer_features_read; + LazyBool m_supports_augmented_libraries_svr4_read; + LazyBool m_supports_jThreadExtendedInfo; bool m_supports_qProcessInfoPID:1, @@ -592,7 +627,9 @@ protected: m_supports_z3:1, m_supports_z4:1, m_supports_QEnvironment:1, - m_supports_QEnvironmentHexEncoded:1; + m_supports_QEnvironmentHexEncoded:1, + m_supports_qSymbol:1, + m_supports_jThreadsInfo:1; lldb::pid_t m_curr_pid; lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations @@ -603,8 +640,8 @@ protected: // If we need to send a packet while the target is running, the m_async_XXX // member variables take care of making this happen. - lldb_private::Mutex m_async_mutex; - lldb_private::Predicate<bool> m_async_packet_predicate; + Mutex m_async_mutex; + Predicate<bool> m_async_packet_predicate; std::string m_async_packet; PacketResult m_async_result; StringExtractorGDBRemote m_async_response; @@ -613,8 +650,8 @@ protected: std::string m_partial_profile_data; std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map; - lldb_private::ArchSpec m_host_arch; - lldb_private::ArchSpec m_process_arch; + ArchSpec m_host_arch; + ArchSpec m_process_arch; uint32_t m_os_version_major; uint32_t m_os_version_minor; uint32_t m_os_version_update; @@ -625,10 +662,11 @@ protected: uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if qGDBServerVersion is not supported uint32_t m_default_packet_timeout; uint64_t m_max_packet_size; // as returned by qSupported + bool DecodeProcessInfoResponse (StringExtractorGDBRemote &response, - lldb_private::ProcessInstanceInfo &process_info); + ProcessInstanceInfo &process_info); private: //------------------------------------------------------------------ // For GDBRemoteCommunicationClient only @@ -636,4 +674,7 @@ private: DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationClient); }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // liblldb_GDBRemoteCommunicationClient_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index dd920c0df0ca..13ce9b2dc5c3 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -12,133 +12,35 @@ #include "lldb/Host/Config.h" #include "GDBRemoteCommunicationServer.h" -#include "lldb/Core/StreamGDBRemote.h" // C Includes // C++ Includes #include <cstring> -#include <chrono> -#include <thread> - -// Other libraries and framework includes -#include "llvm/ADT/Triple.h" -#include "lldb/Interpreter/Args.h" -#include "lldb/Core/Debugger.h" -#include "lldb/Core/Log.h" -#include "lldb/Core/State.h" -#include "lldb/Core/StreamString.h" -#include "lldb/Host/ConnectionFileDescriptor.h" -#include "lldb/Host/Debug.h" -#include "lldb/Host/Endian.h" -#include "lldb/Host/File.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Host/Host.h" -#include "lldb/Host/HostInfo.h" -#include "lldb/Host/StringConvert.h" -#include "lldb/Host/TimeValue.h" -#include "lldb/Target/FileAction.h" -#include "lldb/Target/Platform.h" -#include "lldb/Target/Process.h" -#include "lldb/Host/common/NativeRegisterContext.h" -#include "lldb/Host/common/NativeProcessProtocol.h" -#include "lldb/Host/common/NativeThreadProtocol.h" // Project includes -#include "Utility/StringExtractorGDBRemote.h" -#include "Utility/UriParser.h" -#include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" +#include "Utility/StringExtractorGDBRemote.h" using namespace lldb; using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; -//---------------------------------------------------------------------- -// GDBRemote Errors -//---------------------------------------------------------------------- - -namespace -{ - enum GDBRemoteServerError - { - // Set to the first unused error number in literal form below - eErrorFirst = 29, - eErrorNoProcess = eErrorFirst, - eErrorResume, - eErrorExitStatus - }; -} - -//---------------------------------------------------------------------- -// GDBRemoteCommunicationServer constructor -//---------------------------------------------------------------------- -GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) : - GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform), - m_platform_sp (Platform::GetHostPlatform ()), - m_async_thread (LLDB_INVALID_HOST_THREAD), - m_process_launch_info (), - m_process_launch_error (), - m_spawned_pids (), - m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), - m_proc_infos (), - m_proc_infos_index (0), - m_port_map (), - m_port_offset(0), - m_current_tid (LLDB_INVALID_THREAD_ID), - m_continue_tid (LLDB_INVALID_THREAD_ID), - m_debugged_process_mutex (Mutex::eMutexTypeRecursive), - m_debugged_process_sp (), - m_debugger_sp (), - m_stdio_communication ("process.stdio"), - m_exit_now (false), - m_inferior_prev_state (StateType::eStateInvalid), - m_thread_suffix_supported (false), - m_list_threads_in_stop_reply (false), - m_active_auxv_buffer_sp (), - m_saved_registers_mutex (), - m_saved_registers_map (), - m_next_saved_registers_id (1) +GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(const char *comm_name, + const char *listener_name) : + GDBRemoteCommunication (comm_name, listener_name), + m_exit_now (false) { - assert(is_platform && "must be lldb-platform if debugger is not specified"); } -GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform, - const lldb::PlatformSP& platform_sp, - lldb::DebuggerSP &debugger_sp) : - GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform), - m_platform_sp (platform_sp), - m_async_thread (LLDB_INVALID_HOST_THREAD), - m_process_launch_info (), - m_process_launch_error (), - m_spawned_pids (), - m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), - m_proc_infos (), - m_proc_infos_index (0), - m_port_map (), - m_port_offset(0), - m_current_tid (LLDB_INVALID_THREAD_ID), - m_continue_tid (LLDB_INVALID_THREAD_ID), - m_debugged_process_mutex (Mutex::eMutexTypeRecursive), - m_debugged_process_sp (), - m_debugger_sp (debugger_sp), - m_stdio_communication ("process.stdio"), - m_exit_now (false), - m_inferior_prev_state (StateType::eStateInvalid), - m_thread_suffix_supported (false), - m_list_threads_in_stop_reply (false), - m_active_auxv_buffer_sp (), - m_saved_registers_mutex (), - m_saved_registers_map (), - m_next_saved_registers_id (1) +GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() { - assert(platform_sp); - assert((is_platform || debugger_sp) && "must specify non-NULL debugger_sp when lldb-gdbserver"); } -//---------------------------------------------------------------------- -// Destructor -//---------------------------------------------------------------------- -GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() +void GDBRemoteCommunicationServer::RegisterPacketHandler( + StringExtractorGDBRemote::ServerPacketType packet_type, + PacketHandler handler) { + m_packet_handlers[packet_type] = std::move(handler); } GDBRemoteCommunication::PacketResult @@ -149,7 +51,7 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, { StringExtractorGDBRemote packet; - PacketResult packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec); + PacketResult packet_result = WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec, false); if (packet_result == PacketResult::Success) { const StringExtractorGDBRemote::ServerPacketType packet_type = packet.GetServerPacketType (); @@ -164,288 +66,16 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, quit = true; break; - default: case StringExtractorGDBRemote::eServerPacketType_unimplemented: packet_result = SendUnimplementedResponse (packet.GetStringRef().c_str()); break; - case StringExtractorGDBRemote::eServerPacketType_A: - packet_result = Handle_A (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qfProcessInfo: - packet_result = Handle_qfProcessInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qsProcessInfo: - packet_result = Handle_qsProcessInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qC: - packet_result = Handle_qC (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qHostInfo: - packet_result = Handle_qHostInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer: - packet_result = Handle_qLaunchGDBServer (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess: - packet_result = Handle_qKillSpawnedProcess (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_k: - packet_result = Handle_k (packet); - quit = true; - break; - - case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess: - packet_result = Handle_qLaunchSuccess (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qGroupName: - packet_result = Handle_qGroupName (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qProcessInfo: - packet_result = Handle_qProcessInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID: - packet_result = Handle_qProcessInfoPID (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qSpeedTest: - packet_result = Handle_qSpeedTest (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qUserName: - packet_result = Handle_qUserName (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir: - packet_result = Handle_qGetWorkingDir(packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QEnvironment: - packet_result = Handle_QEnvironment (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QLaunchArch: - packet_result = Handle_QLaunchArch (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR: - packet_result = Handle_QSetDisableASLR (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError: - packet_result = Handle_QSetDetachOnError (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN: - packet_result = Handle_QSetSTDIN (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT: - packet_result = Handle_QSetSTDOUT (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSetSTDERR: - packet_result = Handle_QSetSTDERR (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir: - packet_result = Handle_QSetWorkingDir (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode: - packet_result = Handle_QStartNoAckMode (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir: - packet_result = Handle_qPlatform_mkdir (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod: - packet_result = Handle_qPlatform_chmod (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qPlatform_shell: - packet_result = Handle_qPlatform_shell (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo: - packet_result = Handle_qWatchpointSupportInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_C: - packet_result = Handle_C (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_c: - packet_result = Handle_c (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vCont: - packet_result = Handle_vCont (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vCont_actions: - packet_result = Handle_vCont_actions (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_stop_reason: // ? - packet_result = Handle_stop_reason (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_open: - packet_result = Handle_vFile_Open (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_close: - packet_result = Handle_vFile_Close (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_pread: - packet_result = Handle_vFile_pRead (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_pwrite: - packet_result = Handle_vFile_pWrite (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_size: - packet_result = Handle_vFile_Size (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_mode: - packet_result = Handle_vFile_Mode (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_exists: - packet_result = Handle_vFile_Exists (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_stat: - packet_result = Handle_vFile_Stat (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_md5: - packet_result = Handle_vFile_MD5 (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_symlink: - packet_result = Handle_vFile_symlink (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vFile_unlink: - packet_result = Handle_vFile_unlink (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qRegisterInfo: - packet_result = Handle_qRegisterInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qfThreadInfo: - packet_result = Handle_qfThreadInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qsThreadInfo: - packet_result = Handle_qsThreadInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_p: - packet_result = Handle_p (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_P: - packet_result = Handle_P (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_H: - packet_result = Handle_H (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_I: - packet_result = Handle_I (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_m: - packet_result = Handle_m (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_M: - packet_result = Handle_M (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported: - packet_result = Handle_qMemoryRegionInfoSupported (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo: - packet_result = Handle_qMemoryRegionInfo (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_interrupt: - if (IsGdbServer ()) - packet_result = Handle_interrupt (packet); + default: + auto handler_it = m_packet_handlers.find(packet_type); + if (handler_it == m_packet_handlers.end()) + packet_result = SendUnimplementedResponse (packet.GetStringRef().c_str()); else - { - error.SetErrorString("interrupt received"); - interrupt = true; - } - break; - - case StringExtractorGDBRemote::eServerPacketType_Z: - packet_result = Handle_Z (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_z: - packet_result = Handle_z (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_s: - packet_result = Handle_s (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qSupported: - packet_result = Handle_qSupported (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QThreadSuffixSupported: - packet_result = Handle_QThreadSuffixSupported (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QListThreadsInStopReply: - packet_result = Handle_QListThreadsInStopReply (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read: - packet_result = Handle_qXfer_auxv_read (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState: - packet_result = Handle_QSaveRegisterState (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState: - packet_result = Handle_QRestoreRegisterState (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_vAttach: - packet_result = Handle_vAttach (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_D: - packet_result = Handle_D (packet); - break; - - case StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo: - packet_result = Handle_qThreadStopInfo (packet); + packet_result = handler_it->second (packet, error, interrupt, quit); break; } } @@ -469,729 +99,6 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, return packet_result; } -lldb_private::Error -GDBRemoteCommunicationServer::SetLaunchArguments (const char *const args[], int argc) -{ - if ((argc < 1) || !args || !args[0] || !args[0][0]) - return lldb_private::Error ("%s: no process command line specified to launch", __FUNCTION__); - - m_process_launch_info.SetArguments (const_cast<const char**> (args), true); - return lldb_private::Error (); -} - -lldb_private::Error -GDBRemoteCommunicationServer::SetLaunchFlags (unsigned int launch_flags) -{ - m_process_launch_info.GetFlags ().Set (launch_flags); - return lldb_private::Error (); -} - -lldb_private::Error -GDBRemoteCommunicationServer::LaunchProcess () -{ - // FIXME This looks an awful lot like we could override this in - // derived classes, one for lldb-platform, the other for lldb-gdbserver. - if (IsGdbServer ()) - return LaunchProcessForDebugging (); - else - return LaunchPlatformProcess (); -} - -bool -GDBRemoteCommunicationServer::ShouldRedirectInferiorOutputOverGdbRemote (const lldb_private::ProcessLaunchInfo &launch_info) const -{ - // Retrieve the file actions specified for stdout and stderr. - auto stdout_file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); - auto stderr_file_action = launch_info.GetFileActionForFD (STDERR_FILENO); - - // If neither stdout and stderr file actions are specified, we're not doing anything special, so - // assume we want to redirect stdout/stderr over gdb-remote $O messages. - if ((stdout_file_action == nullptr) && (stderr_file_action == nullptr)) - { - // Send stdout/stderr over the gdb-remote protocol. - return true; - } - - // Any other setting for either stdout or stderr implies we are either suppressing - // it (with /dev/null) or we've got it set to a PTY. Either way, we don't want the - // output over gdb-remote. - return false; -} - -lldb_private::Error -GDBRemoteCommunicationServer::LaunchProcessForDebugging () -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - if (!m_process_launch_info.GetArguments ().GetArgumentCount ()) - return lldb_private::Error ("%s: no process command line specified to launch", __FUNCTION__); - - lldb_private::Error error; - { - Mutex::Locker locker (m_debugged_process_mutex); - assert (!m_debugged_process_sp && "lldb-gdbserver creating debugged process but one already exists"); - error = m_platform_sp->LaunchNativeProcess ( - m_process_launch_info, - *this, - m_debugged_process_sp); - } - - if (!error.Success ()) - { - fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0)); - return error; - } - - // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as needed. - // llgs local-process debugging may specify PTYs, which will eliminate the need to reflect inferior - // stdout/stderr over the gdb-remote protocol. - if (ShouldRedirectInferiorOutputOverGdbRemote (m_process_launch_info)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " setting up stdout/stderr redirection via $O gdb-remote commands", __FUNCTION__, m_debugged_process_sp->GetID ()); - - // Setup stdout/stderr mapping from inferior to $O - auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor (); - if (terminal_fd >= 0) - { - if (log) - log->Printf ("ProcessGDBRemoteCommunicationServer::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd); - error = SetSTDIOFileDescriptor (terminal_fd); - if (error.Fail ()) - return error; - } - else - { - if (log) - log->Printf ("ProcessGDBRemoteCommunicationServer::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); - } - } - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " skipping stdout/stderr redirection via $O: inferior will communicate over client-provided file descriptors", __FUNCTION__, m_debugged_process_sp->GetID ()); - } - - printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID ()); - - // Add to list of spawned processes. - lldb::pid_t pid; - if ((pid = m_process_launch_info.GetProcessID ()) != LLDB_INVALID_PROCESS_ID) - { - // add to spawned pids - Mutex::Locker locker (m_spawned_pids_mutex); - // On an lldb-gdbserver, we would expect there to be only one. - assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed"); - m_spawned_pids.insert (pid); - } - - return error; -} - -lldb_private::Error -GDBRemoteCommunicationServer::LaunchPlatformProcess () -{ - if (!m_process_launch_info.GetArguments ().GetArgumentCount ()) - return lldb_private::Error ("%s: no process command line specified to launch", __FUNCTION__); - - // specify the process monitor if not already set. This should - // generally be what happens since we need to reap started - // processes. - if (!m_process_launch_info.GetMonitorProcessCallback ()) - m_process_launch_info.SetMonitorProcessCallback(ReapDebuggedProcess, this, false); - - lldb_private::Error error = m_platform_sp->LaunchProcess (m_process_launch_info); - if (!error.Success ()) - { - fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0)); - return error; - } - - printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID()); - - // add to list of spawned processes. On an lldb-gdbserver, we - // would expect there to be only one. - const auto pid = m_process_launch_info.GetProcessID(); - if (pid != LLDB_INVALID_PROCESS_ID) - { - // add to spawned pids - Mutex::Locker locker (m_spawned_pids_mutex); - m_spawned_pids.insert(pid); - } - - return error; -} - -lldb_private::Error -GDBRemoteCommunicationServer::AttachToProcess (lldb::pid_t pid) -{ - Error error; - - if (!IsGdbServer ()) - { - error.SetErrorString("cannot AttachToProcess () unless process is lldb-gdbserver"); - return error; - } - - Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64, __FUNCTION__, pid); - - // Scope for mutex locker. - { - // Before we try to attach, make sure we aren't already monitoring something else. - Mutex::Locker locker (m_spawned_pids_mutex); - if (!m_spawned_pids.empty ()) - { - error.SetErrorStringWithFormat ("cannot attach to a process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", pid, *m_spawned_pids.begin()); - return error; - } - - // Try to attach. - error = m_platform_sp->AttachNativeProcess (pid, *this, m_debugged_process_sp); - if (!error.Success ()) - { - fprintf (stderr, "%s: failed to attach to process %" PRIu64 ": %s", __FUNCTION__, pid, error.AsCString ()); - return error; - } - - // Setup stdout/stderr mapping from inferior. - auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor (); - if (terminal_fd >= 0) - { - if (log) - log->Printf ("ProcessGDBRemoteCommunicationServer::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd); - error = SetSTDIOFileDescriptor (terminal_fd); - if (error.Fail ()) - return error; - } - else - { - if (log) - log->Printf ("ProcessGDBRemoteCommunicationServer::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); - } - - printf ("Attached to process %" PRIu64 "...\n", pid); - - // Add to list of spawned processes. - assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed"); - m_spawned_pids.insert (pid); - - return error; - } -} - -void -GDBRemoteCommunicationServer::InitializeDelegate (lldb_private::NativeProcessProtocol *process) -{ - assert (process && "process cannot be NULL"); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s called with NativeProcessProtocol pid %" PRIu64 ", current state: %s", - __FUNCTION__, - process->GetID (), - StateAsCString (process->GetState ())); - } -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::SendWResponse (lldb_private::NativeProcessProtocol *process) -{ - assert (process && "process cannot be NULL"); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // send W notification - ExitType exit_type = ExitType::eExitTypeInvalid; - int return_code = 0; - std::string exit_description; - - const bool got_exit_info = process->GetExitStatus (&exit_type, &return_code, exit_description); - if (!got_exit_info) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 ", failed to retrieve process exit status", __FUNCTION__, process->GetID ()); - - StreamGDBRemote response; - response.PutChar ('E'); - response.PutHex8 (GDBRemoteServerError::eErrorExitStatus); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 ", returning exit type %d, return code %d [%s]", __FUNCTION__, process->GetID (), exit_type, return_code, exit_description.c_str ()); - - StreamGDBRemote response; - - char return_type_code; - switch (exit_type) - { - case ExitType::eExitTypeExit: - return_type_code = 'W'; - break; - case ExitType::eExitTypeSignal: - return_type_code = 'X'; - break; - case ExitType::eExitTypeStop: - return_type_code = 'S'; - break; - case ExitType::eExitTypeInvalid: - return_type_code = 'E'; - break; - } - response.PutChar (return_type_code); - - // POSIX exit status limited to unsigned 8 bits. - response.PutHex8 (return_code); - - return SendPacketNoLock(response.GetData(), response.GetSize()); - } -} - -static void -AppendHexValue (StreamString &response, const uint8_t* buf, uint32_t buf_size, bool swap) -{ - int64_t i; - if (swap) - { - for (i = buf_size-1; i >= 0; i--) - response.PutHex8 (buf[i]); - } - else - { - for (i = 0; i < buf_size; i++) - response.PutHex8 (buf[i]); - } -} - -static void -WriteRegisterValueInHexFixedWidth (StreamString &response, - NativeRegisterContextSP ®_ctx_sp, - const RegisterInfo ®_info, - const RegisterValue *reg_value_p) -{ - RegisterValue reg_value; - if (!reg_value_p) - { - Error error = reg_ctx_sp->ReadRegister (®_info, reg_value); - if (error.Success ()) - reg_value_p = ®_value; - // else log. - } - - if (reg_value_p) - { - AppendHexValue (response, (const uint8_t*) reg_value_p->GetBytes (), reg_value_p->GetByteSize (), false); - } - else - { - // Zero-out any unreadable values. - if (reg_info.byte_size > 0) - { - std::basic_string<uint8_t> zeros(reg_info.byte_size, '\0'); - AppendHexValue (response, zeros.data(), zeros.size(), false); - } - } -} - -// WriteGdbRegnumWithFixedWidthHexRegisterValue (response, reg_ctx_sp, *reg_info_p, reg_value); - - -static void -WriteGdbRegnumWithFixedWidthHexRegisterValue (StreamString &response, - NativeRegisterContextSP ®_ctx_sp, - const RegisterInfo ®_info, - const RegisterValue ®_value) -{ - // Output the register number as 'NN:VVVVVVVV;' where NN is a 2 bytes HEX - // gdb register number, and VVVVVVVV is the correct number of hex bytes - // as ASCII for the register value. - if (reg_info.kinds[eRegisterKindGDB] == LLDB_INVALID_REGNUM) - return; - - response.Printf ("%.02x:", reg_info.kinds[eRegisterKindGDB]); - WriteRegisterValueInHexFixedWidth (response, reg_ctx_sp, reg_info, ®_value); - response.PutChar (';'); -} - - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::SendStopReplyPacketForThread (lldb::tid_t tid) -{ - Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); - - // Ensure we're llgs. - if (!IsGdbServer ()) - { - // Only supported on llgs - return SendUnimplementedResponse (""); - } - - // Ensure we have a debugged process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - return SendErrorResponse (50); - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s preparing packet for pid %" PRIu64 " tid %" PRIu64, - __FUNCTION__, m_debugged_process_sp->GetID (), tid); - - // Ensure we can get info on the given thread. - NativeThreadProtocolSP thread_sp (m_debugged_process_sp->GetThreadByID (tid)); - if (!thread_sp) - return SendErrorResponse (51); - - // Grab the reason this thread stopped. - struct ThreadStopInfo tid_stop_info; - std::string description; - if (!thread_sp->GetStopReason (tid_stop_info, description)) - return SendErrorResponse (52); - - // FIXME implement register handling for exec'd inferiors. - // if (tid_stop_info.reason == eStopReasonExec) - // { - // const bool force = true; - // InitializeRegisters(force); - // } - - StreamString response; - // Output the T packet with the thread - response.PutChar ('T'); - int signum = tid_stop_info.details.signal.signo; - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64, - __FUNCTION__, - m_debugged_process_sp->GetID (), - tid, - signum, - tid_stop_info.reason, - tid_stop_info.details.exception.type); - } - - // Print the signal number. - response.PutHex8 (signum & 0xff); - - // Include the tid. - response.Printf ("thread:%" PRIx64 ";", tid); - - // Include the thread name if there is one. - const std::string thread_name = thread_sp->GetName (); - if (!thread_name.empty ()) - { - size_t thread_name_len = thread_name.length (); - - if (::strcspn (thread_name.c_str (), "$#+-;:") == thread_name_len) - { - response.PutCString ("name:"); - response.PutCString (thread_name.c_str ()); - } - else - { - // The thread name contains special chars, send as hex bytes. - response.PutCString ("hexname:"); - response.PutCStringAsRawHex8 (thread_name.c_str ()); - } - response.PutChar (';'); - } - - // If a 'QListThreadsInStopReply' was sent to enable this feature, we - // will send all thread IDs back in the "threads" key whose value is - // a list of hex thread IDs separated by commas: - // "threads:10a,10b,10c;" - // This will save the debugger from having to send a pair of qfThreadInfo - // and qsThreadInfo packets, but it also might take a lot of room in the - // stop reply packet, so it must be enabled only on systems where there - // are no limits on packet lengths. - if (m_list_threads_in_stop_reply) - { - response.PutCString ("threads:"); - - uint32_t thread_index = 0; - NativeThreadProtocolSP listed_thread_sp; - for (listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index); listed_thread_sp; ++thread_index, listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index)) - { - if (thread_index > 0) - response.PutChar (','); - response.Printf ("%" PRIx64, listed_thread_sp->GetID ()); - } - response.PutChar (';'); - } - - // - // Expedite registers. - // - - // Grab the register context. - NativeRegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext (); - if (reg_ctx_sp) - { - // Expedite all registers in the first register set (i.e. should be GPRs) that are not contained in other registers. - const RegisterSet *reg_set_p; - if (reg_ctx_sp->GetRegisterSetCount () > 0 && ((reg_set_p = reg_ctx_sp->GetRegisterSet (0)) != nullptr)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s expediting registers from set '%s' (registers set count: %zu)", __FUNCTION__, reg_set_p->name ? reg_set_p->name : "<unnamed-set>", reg_set_p->num_registers); - - for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) - { - const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex (*reg_num_p); - if (reg_info_p == nullptr) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to get register info for register set '%s', register index %" PRIu32, __FUNCTION__, reg_set_p->name ? reg_set_p->name : "<unnamed-set>", *reg_num_p); - } - else if (reg_info_p->value_regs == nullptr) - { - // Only expediate registers that are not contained in other registers. - RegisterValue reg_value; - Error error = reg_ctx_sp->ReadRegister (reg_info_p, reg_value); - if (error.Success ()) - WriteGdbRegnumWithFixedWidthHexRegisterValue (response, reg_ctx_sp, *reg_info_p, reg_value); - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "<unnamed-register>", *reg_num_p, error.AsCString ()); - - } - } - } - } - } - - const char* reason_str = nullptr; - switch (tid_stop_info.reason) - { - case eStopReasonTrace: - reason_str = "trace"; - break; - case eStopReasonBreakpoint: - reason_str = "breakpoint"; - break; - case eStopReasonWatchpoint: - reason_str = "watchpoint"; - break; - case eStopReasonSignal: - reason_str = "signal"; - break; - case eStopReasonException: - reason_str = "exception"; - break; - case eStopReasonExec: - reason_str = "exec"; - break; - case eStopReasonInstrumentation: - case eStopReasonInvalid: - case eStopReasonPlanComplete: - case eStopReasonThreadExiting: - case eStopReasonNone: - break; - } - if (reason_str != nullptr) - { - response.Printf ("reason:%s;", reason_str); - } - - if (!description.empty()) - { - // Description may contains special chars, send as hex bytes. - response.PutCString ("description:"); - response.PutCStringAsRawHex8 (description.c_str ()); - response.PutChar (';'); - } - else if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) - { - response.PutCString ("metype:"); - response.PutHex64 (tid_stop_info.details.exception.type); - response.PutCString (";mecount:"); - response.PutHex32 (tid_stop_info.details.exception.data_count); - response.PutChar (';'); - - for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) - { - response.PutCString ("medata:"); - response.PutHex64 (tid_stop_info.details.exception.data[i]); - response.PutChar (';'); - } - } - - return SendPacketNoLock (response.GetData(), response.GetSize()); -} - -void -GDBRemoteCommunicationServer::HandleInferiorState_Exited (lldb_private::NativeProcessProtocol *process) -{ - assert (process && "process cannot be NULL"); - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s called", __FUNCTION__); - - // Send the exit result, and don't flush output. - // Note: flushing output here would join the inferior stdio reflection thread, which - // would gunk up the waitpid monitor thread that is calling this. - PacketResult result = SendStopReasonForState (StateType::eStateExited, false); - if (result != PacketResult::Success) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to send stop notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID ()); - } - - // Remove the process from the list of spawned pids. - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.erase (process->GetID ()) < 1) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to remove PID %" PRIu64 " from the spawned pids list", __FUNCTION__, process->GetID ()); - - } - } - - // FIXME can't do this yet - since process state propagation is currently - // synchronous, it is running off the NativeProcessProtocol's innards and - // will tear down the NPP while it still has code to execute. -#if 0 - // Clear the NativeProcessProtocol pointer. - { - Mutex::Locker locker (m_debugged_process_mutex); - m_debugged_process_sp.reset(); - } -#endif - - // Close the pipe to the inferior terminal i/o if we launched it - // and set one up. Otherwise, 'k' and its flush of stdio could - // end up waiting on a thread join that will never end. Consider - // adding a timeout to the connection thread join call so we - // can avoid that scenario altogether. - MaybeCloseInferiorTerminalConnection (); - - // We are ready to exit the debug monitor. - m_exit_now = true; -} - -void -GDBRemoteCommunicationServer::HandleInferiorState_Stopped (lldb_private::NativeProcessProtocol *process) -{ - assert (process && "process cannot be NULL"); - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s called", __FUNCTION__); - - // Send the stop reason unless this is the stop after the - // launch or attach. - switch (m_inferior_prev_state) - { - case eStateLaunching: - case eStateAttaching: - // Don't send anything per debugserver behavior. - break; - default: - // In all other cases, send the stop reason. - PacketResult result = SendStopReasonForState (StateType::eStateStopped, false); - if (result != PacketResult::Success) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to send stop notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID ()); - } - break; - } -} - -void -GDBRemoteCommunicationServer::ProcessStateChanged (lldb_private::NativeProcessProtocol *process, lldb::StateType state) -{ - assert (process && "process cannot be NULL"); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s called with NativeProcessProtocol pid %" PRIu64 ", state: %s", - __FUNCTION__, - process->GetID (), - StateAsCString (state)); - } - - switch (state) - { - case StateType::eStateExited: - HandleInferiorState_Exited (process); - break; - - case StateType::eStateStopped: - HandleInferiorState_Stopped (process); - break; - - default: - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s didn't handle state change for pid %" PRIu64 ", new state: %s", - __FUNCTION__, - process->GetID (), - StateAsCString (state)); - } - break; - } - - // Remember the previous state reported to us. - m_inferior_prev_state = state; -} - -void -GDBRemoteCommunicationServer::DidExec (NativeProcessProtocol *process) -{ - ClearProcessSpecificData (); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::SendONotification (const char *buffer, uint32_t len) -{ - if ((buffer == nullptr) || (len == 0)) - { - // Nothing to send. - return PacketResult::Success; - } - - StreamString response; - response.PutChar ('O'); - response.PutBytesAsRawHex8 (buffer, len); - - return SendPacketNoLock (response.GetData (), response.GetSize ()); -} - -lldb_private::Error -GDBRemoteCommunicationServer::SetSTDIOFileDescriptor (int fd) -{ - Error error; - - // Set up the Read Thread for reading/handling process I/O - std::unique_ptr<ConnectionFileDescriptor> conn_up (new ConnectionFileDescriptor (fd, true)); - if (!conn_up) - { - error.SetErrorString ("failed to create ConnectionFileDescriptor"); - return error; - } - - m_stdio_communication.SetConnection (conn_up.release()); - if (!m_stdio_communication.IsConnected ()) - { - error.SetErrorString ("failed to set connection for inferior I/O communication"); - return error; - } - - m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this); - m_stdio_communication.StartReadThread(); - - return error; -} - -void -GDBRemoteCommunicationServer::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len) -{ - GDBRemoteCommunicationServer *server = reinterpret_cast<GDBRemoteCommunicationServer*> (baton); - static_cast<void> (server->SendONotification (static_cast<const char *>(src), src_len)); -} - GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServer::SendUnimplementedResponse (const char *) { @@ -1229,3334 +136,3 @@ GDBRemoteCommunicationServer::HandshakeWithClient(Error *error_ptr) { return GetAck() == PacketResult::Success; } - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet) -{ - StreamString response; - - // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00 - - ArchSpec host_arch(HostInfo::GetArchitecture()); - const llvm::Triple &host_triple = host_arch.GetTriple(); - response.PutCString("triple:"); - response.PutCStringAsRawHex8(host_triple.getTriple().c_str()); - response.Printf (";ptrsize:%u;",host_arch.GetAddressByteSize()); - - const char* distribution_id = host_arch.GetDistributionId ().AsCString (); - if (distribution_id) - { - response.PutCString("distribution_id:"); - response.PutCStringAsRawHex8(distribution_id); - response.PutCString(";"); - } - - // Only send out MachO info when lldb-platform/llgs is running on a MachO host. -#if defined(__APPLE__) - uint32_t cpu = host_arch.GetMachOCPUType(); - uint32_t sub = host_arch.GetMachOCPUSubType(); - if (cpu != LLDB_INVALID_CPUTYPE) - response.Printf ("cputype:%u;", cpu); - if (sub != LLDB_INVALID_CPUTYPE) - response.Printf ("cpusubtype:%u;", sub); - - if (cpu == ArchSpec::kCore_arm_any) - response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes. - else - response.Printf("watchpoint_exceptions_received:after;"); -#else - response.Printf("watchpoint_exceptions_received:after;"); -#endif - - switch (lldb::endian::InlHostByteOrder()) - { - case eByteOrderBig: response.PutCString ("endian:big;"); break; - case eByteOrderLittle: response.PutCString ("endian:little;"); break; - case eByteOrderPDP: response.PutCString ("endian:pdp;"); break; - default: response.PutCString ("endian:unknown;"); break; - } - - uint32_t major = UINT32_MAX; - uint32_t minor = UINT32_MAX; - uint32_t update = UINT32_MAX; - if (HostInfo::GetOSVersion(major, minor, update)) - { - if (major != UINT32_MAX) - { - response.Printf("os_version:%u", major); - if (minor != UINT32_MAX) - { - response.Printf(".%u", minor); - if (update != UINT32_MAX) - response.Printf(".%u", update); - } - response.PutChar(';'); - } - } - - std::string s; - if (HostInfo::GetOSBuildString(s)) - { - response.PutCString ("os_build:"); - response.PutCStringAsRawHex8(s.c_str()); - response.PutChar(';'); - } - if (HostInfo::GetOSKernelDescription(s)) - { - response.PutCString ("os_kernel:"); - response.PutCStringAsRawHex8(s.c_str()); - response.PutChar(';'); - } - -#if defined(__APPLE__) - -#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - // For iOS devices, we are connected through a USB Mux so we never pretend - // to actually have a hostname as far as the remote lldb that is connecting - // to this lldb-platform is concerned - response.PutCString ("hostname:"); - response.PutCStringAsRawHex8("127.0.0.1"); - response.PutChar(';'); -#else // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - if (HostInfo::GetHostname(s)) - { - response.PutCString ("hostname:"); - response.PutCStringAsRawHex8(s.c_str()); - response.PutChar(';'); - } -#endif // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) - -#else // #if defined(__APPLE__) - if (HostInfo::GetHostname(s)) - { - response.PutCString ("hostname:"); - response.PutCStringAsRawHex8(s.c_str()); - response.PutChar(';'); - } -#endif // #if defined(__APPLE__) - - return SendPacketNoLock (response.GetData(), response.GetSize()); -} - -static void -CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, StreamString &response) -{ - response.Printf ("pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;", - proc_info.GetProcessID(), - proc_info.GetParentProcessID(), - proc_info.GetUserID(), - proc_info.GetGroupID(), - proc_info.GetEffectiveUserID(), - proc_info.GetEffectiveGroupID()); - response.PutCString ("name:"); - response.PutCStringAsRawHex8(proc_info.GetName()); - response.PutChar(';'); - const ArchSpec &proc_arch = proc_info.GetArchitecture(); - if (proc_arch.IsValid()) - { - const llvm::Triple &proc_triple = proc_arch.GetTriple(); - response.PutCString("triple:"); - response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); - response.PutChar(';'); - } -} - -static void -CreateProcessInfoResponse_DebugServerStyle (const ProcessInstanceInfo &proc_info, StreamString &response) -{ - response.Printf ("pid:%" PRIx64 ";parent-pid:%" PRIx64 ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;", - proc_info.GetProcessID(), - proc_info.GetParentProcessID(), - proc_info.GetUserID(), - proc_info.GetGroupID(), - proc_info.GetEffectiveUserID(), - proc_info.GetEffectiveGroupID()); - - const ArchSpec &proc_arch = proc_info.GetArchitecture(); - if (proc_arch.IsValid()) - { - const llvm::Triple &proc_triple = proc_arch.GetTriple(); -#if defined(__APPLE__) - // We'll send cputype/cpusubtype. - const uint32_t cpu_type = proc_arch.GetMachOCPUType(); - if (cpu_type != 0) - response.Printf ("cputype:%" PRIx32 ";", cpu_type); - - const uint32_t cpu_subtype = proc_arch.GetMachOCPUSubType(); - if (cpu_subtype != 0) - response.Printf ("cpusubtype:%" PRIx32 ";", cpu_subtype); - - - const std::string vendor = proc_triple.getVendorName (); - if (!vendor.empty ()) - response.Printf ("vendor:%s;", vendor.c_str ()); -#else - // We'll send the triple. - response.PutCString("triple:"); - response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); - response.PutChar(';'); - -#endif - std::string ostype = proc_triple.getOSName (); - // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64. - if (proc_triple.getVendor () == llvm::Triple::Apple) - { - switch (proc_triple.getArch ()) - { - case llvm::Triple::arm: - case llvm::Triple::aarch64: - ostype = "ios"; - break; - default: - // No change. - break; - } - } - response.Printf ("ostype:%s;", ostype.c_str ()); - - - switch (proc_arch.GetByteOrder ()) - { - case lldb::eByteOrderLittle: response.PutCString ("endian:little;"); break; - case lldb::eByteOrderBig: response.PutCString ("endian:big;"); break; - case lldb::eByteOrderPDP: response.PutCString ("endian:pdp;"); break; - default: - // Nothing. - break; - } - - if (proc_triple.isArch64Bit ()) - response.PutCString ("ptrsize:8;"); - else if (proc_triple.isArch32Bit ()) - response.PutCString ("ptrsize:4;"); - else if (proc_triple.isArch16Bit ()) - response.PutCString ("ptrsize:2;"); - } - -} - - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qProcessInfo (StringExtractorGDBRemote &packet) -{ - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - - if (IsGdbServer ()) - { - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - return SendErrorResponse (68); - - pid = m_debugged_process_sp->GetID (); - } - else if (m_is_platform) - { - pid = m_process_launch_info.GetProcessID (); - m_process_launch_info.Clear (); - } - else - return SendUnimplementedResponse (packet.GetStringRef ().c_str ()); - - if (pid == LLDB_INVALID_PROCESS_ID) - return SendErrorResponse (1); - - ProcessInstanceInfo proc_info; - if (!Host::GetProcessInfo (pid, proc_info)) - return SendErrorResponse (1); - - StreamString response; - CreateProcessInfoResponse_DebugServerStyle(proc_info, response); - return SendPacketNoLock (response.GetData (), response.GetSize ()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qProcessInfoPID (StringExtractorGDBRemote &packet) -{ - // Packet format: "qProcessInfoPID:%i" where %i is the pid - packet.SetFilePos(::strlen ("qProcessInfoPID:")); - lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID); - if (pid != LLDB_INVALID_PROCESS_ID) - { - ProcessInstanceInfo proc_info; - if (Host::GetProcessInfo(pid, proc_info)) - { - StreamString response; - CreateProcessInfoResponse (proc_info, response); - return SendPacketNoLock (response.GetData(), response.GetSize()); - } - } - return SendErrorResponse (1); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &packet) -{ - m_proc_infos_index = 0; - m_proc_infos.Clear(); - - ProcessInstanceInfoMatch match_info; - packet.SetFilePos(::strlen ("qfProcessInfo")); - if (packet.GetChar() == ':') - { - - std::string key; - std::string value; - while (packet.GetNameColonValue(key, value)) - { - bool success = true; - if (key.compare("name") == 0) - { - StringExtractor extractor; - extractor.GetStringRef().swap(value); - extractor.GetHexByteString (value); - match_info.GetProcessInfo().GetExecutableFile().SetFile(value.c_str(), false); - } - else if (key.compare("name_match") == 0) - { - if (value.compare("equals") == 0) - { - match_info.SetNameMatchType (eNameMatchEquals); - } - else if (value.compare("starts_with") == 0) - { - match_info.SetNameMatchType (eNameMatchStartsWith); - } - else if (value.compare("ends_with") == 0) - { - match_info.SetNameMatchType (eNameMatchEndsWith); - } - else if (value.compare("contains") == 0) - { - match_info.SetNameMatchType (eNameMatchContains); - } - else if (value.compare("regex") == 0) - { - match_info.SetNameMatchType (eNameMatchRegularExpression); - } - else - { - success = false; - } - } - else if (key.compare("pid") == 0) - { - match_info.GetProcessInfo().SetProcessID (StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success)); - } - else if (key.compare("parent_pid") == 0) - { - match_info.GetProcessInfo().SetParentProcessID (StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success)); - } - else if (key.compare("uid") == 0) - { - match_info.GetProcessInfo().SetUserID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); - } - else if (key.compare("gid") == 0) - { - match_info.GetProcessInfo().SetGroupID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); - } - else if (key.compare("euid") == 0) - { - match_info.GetProcessInfo().SetEffectiveUserID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); - } - else if (key.compare("egid") == 0) - { - match_info.GetProcessInfo().SetEffectiveGroupID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); - } - else if (key.compare("all_users") == 0) - { - match_info.SetMatchAllUsers(Args::StringToBoolean(value.c_str(), false, &success)); - } - else if (key.compare("triple") == 0) - { - match_info.GetProcessInfo().GetArchitecture().SetTriple (value.c_str(), NULL); - } - else - { - success = false; - } - - if (!success) - return SendErrorResponse (2); - } - } - - if (Host::FindProcesses (match_info, m_proc_infos)) - { - // We found something, return the first item by calling the get - // subsequent process info packet handler... - return Handle_qsProcessInfo (packet); - } - return SendErrorResponse (3); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &packet) -{ - if (m_proc_infos_index < m_proc_infos.GetSize()) - { - StreamString response; - CreateProcessInfoResponse (m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response); - ++m_proc_infos_index; - return SendPacketNoLock (response.GetData(), response.GetSize()); - } - return SendErrorResponse (4); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet) -{ -#if !defined(LLDB_DISABLE_POSIX) - // Packet format: "qUserName:%i" where %i is the uid - packet.SetFilePos(::strlen ("qUserName:")); - uint32_t uid = packet.GetU32 (UINT32_MAX); - if (uid != UINT32_MAX) - { - std::string name; - if (HostInfo::LookupUserName(uid, name)) - { - StreamString response; - response.PutCStringAsRawHex8 (name.c_str()); - return SendPacketNoLock (response.GetData(), response.GetSize()); - } - } -#endif - return SendErrorResponse (5); - -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qGroupName (StringExtractorGDBRemote &packet) -{ -#if !defined(LLDB_DISABLE_POSIX) - // Packet format: "qGroupName:%i" where %i is the gid - packet.SetFilePos(::strlen ("qGroupName:")); - uint32_t gid = packet.GetU32 (UINT32_MAX); - if (gid != UINT32_MAX) - { - std::string name; - if (HostInfo::LookupGroupName(gid, name)) - { - StreamString response; - response.PutCStringAsRawHex8 (name.c_str()); - return SendPacketNoLock (response.GetData(), response.GetSize()); - } - } -#endif - return SendErrorResponse (6); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qSpeedTest (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("qSpeedTest:")); - - std::string key; - std::string value; - bool success = packet.GetNameColonValue(key, value); - if (success && key.compare("response_size") == 0) - { - uint32_t response_size = StringConvert::ToUInt32(value.c_str(), 0, 0, &success); - if (success) - { - if (response_size == 0) - return SendOKResponse(); - StreamString response; - uint32_t bytes_left = response_size; - response.PutCString("data:"); - while (bytes_left > 0) - { - if (bytes_left >= 26) - { - response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - bytes_left -= 26; - } - else - { - response.Printf ("%*.*s;", bytes_left, bytes_left, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - bytes_left = 0; - } - } - return SendPacketNoLock (response.GetData(), response.GetSize()); - } - } - return SendErrorResponse (7); -} - -// -//static bool -//WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) -//{ -// const int time_delta_usecs = 100000; -// const int num_retries = timeout_in_seconds/time_delta_usecs; -// for (int i=0; i<num_retries; i++) -// { -// struct proc_bsdinfo bsd_info; -// int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, -// (uint64_t) 0, -// &bsd_info, -// PROC_PIDTBSDINFO_SIZE); -// -// switch (error) -// { -// case EINVAL: -// case ENOTSUP: -// case ESRCH: -// case EPERM: -// return false; -// -// default: -// break; -// -// case 0: -// if (bsd_info.pbi_status == SSTOP) -// return true; -// } -// ::usleep (time_delta_usecs); -// } -// return false; -//} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet) -{ - // The 'A' packet is the most over designed packet ever here with - // redundant argument indexes, redundant argument lengths and needed hex - // encoded argument string values. Really all that is needed is a comma - // separated hex encoded argument value list, but we will stay true to the - // documented version of the 'A' packet here... - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - int actual_arg_index = 0; - - packet.SetFilePos(1); // Skip the 'A' - bool success = true; - while (success && packet.GetBytesLeft() > 0) - { - // Decode the decimal argument string length. This length is the - // number of hex nibbles in the argument string value. - const uint32_t arg_len = packet.GetU32(UINT32_MAX); - if (arg_len == UINT32_MAX) - success = false; - else - { - // Make sure the argument hex string length is followed by a comma - if (packet.GetChar() != ',') - success = false; - else - { - // Decode the argument index. We ignore this really because - // who would really send down the arguments in a random order??? - const uint32_t arg_idx = packet.GetU32(UINT32_MAX); - if (arg_idx == UINT32_MAX) - success = false; - else - { - // Make sure the argument index is followed by a comma - if (packet.GetChar() != ',') - success = false; - else - { - // Decode the argument string value from hex bytes - // back into a UTF8 string and make sure the length - // matches the one supplied in the packet - std::string arg; - if (packet.GetHexByteStringFixedLength(arg, arg_len) != (arg_len / 2)) - success = false; - else - { - // If there are any bytes left - if (packet.GetBytesLeft()) - { - if (packet.GetChar() != ',') - success = false; - } - - if (success) - { - if (arg_idx == 0) - m_process_launch_info.GetExecutableFile().SetFile(arg.c_str(), false); - m_process_launch_info.GetArguments().AppendArgument(arg.c_str()); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s added arg %d: \"%s\"", __FUNCTION__, actual_arg_index, arg.c_str ()); - ++actual_arg_index; - } - } - } - } - } - } - } - - if (success) - { - m_process_launch_error = LaunchProcess (); - if (m_process_launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) - { - return SendOKResponse (); - } - else - { - Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf("GDBRemoteCommunicationServer::%s failed to launch exe: %s", - __FUNCTION__, - m_process_launch_error.AsCString()); - - } - } - return SendErrorResponse (8); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet) -{ - StreamString response; - - if (IsGdbServer ()) - { - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - return SendErrorResponse (68); - - // Make sure we set the current thread so g and p packets return - // the data the gdb will expect. - lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID (); - SetCurrentThreadID (tid); - - NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetCurrentThread (); - if (!thread_sp) - return SendErrorResponse (69); - - response.Printf ("QC%" PRIx64, thread_sp->GetID ()); - } - else - { - // NOTE: lldb should now be using qProcessInfo for process IDs. This path here - // should not be used. It is reporting process id instead of thread id. The - // correct answer doesn't seem to make much sense for lldb-platform. - // CONSIDER: flip to "unsupported". - lldb::pid_t pid = m_process_launch_info.GetProcessID(); - response.Printf("QC%" PRIx64, pid); - - // this should always be platform here - assert (m_is_platform && "this code path should only be traversed for lldb-platform"); - - if (m_is_platform) - { - // If we launch a process and this GDB server is acting as a platform, - // then we need to clear the process launch state so we can start - // launching another process. In order to launch a process a bunch or - // packets need to be sent: environment packets, working directory, - // disable ASLR, and many more settings. When we launch a process we - // then need to know when to clear this information. Currently we are - // selecting the 'qC' packet as that packet which seems to make the most - // sense. - if (pid != LLDB_INVALID_PROCESS_ID) - { - m_process_launch_info.Clear(); - } - } - } - return SendPacketNoLock (response.GetData(), response.GetSize()); -} - -bool -GDBRemoteCommunicationServer::DebugserverProcessReaped (lldb::pid_t pid) -{ - Mutex::Locker locker (m_spawned_pids_mutex); - FreePortForProcess(pid); - return m_spawned_pids.erase(pid) > 0; -} -bool -GDBRemoteCommunicationServer::ReapDebugserverProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, // Zero for no signal - int status) // Exit value of process if signal is zero -{ - GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton; - server->DebugserverProcessReaped (pid); - return true; -} - -bool -GDBRemoteCommunicationServer::DebuggedProcessReaped (lldb::pid_t pid) -{ - // reap a process that we were debugging (but not debugserver) - Mutex::Locker locker (m_spawned_pids_mutex); - return m_spawned_pids.erase(pid) > 0; -} - -bool -GDBRemoteCommunicationServer::ReapDebuggedProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, // Zero for no signal - int status) // Exit value of process if signal is zero -{ - GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton; - server->DebuggedProcessReaped (pid); - return true; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) -{ -#ifdef _WIN32 - return SendErrorResponse(9); -#else - Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); - - // Spawn a local debugserver as a platform so we can then attach or launch - // a process... - - if (m_is_platform) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() called", __FUNCTION__); - - // Sleep and wait a bit for debugserver to start to listen... - ConnectionFileDescriptor file_conn; - std::string hostname; - // TODO: /tmp/ should not be hardcoded. User might want to override /tmp - // with the TMPDIR environment variable - packet.SetFilePos(::strlen ("qLaunchGDBServer;")); - std::string name; - std::string value; - uint16_t port = UINT16_MAX; - while (packet.GetNameColonValue(name, value)) - { - if (name.compare ("host") == 0) - hostname.swap(value); - else if (name.compare ("port") == 0) - port = StringConvert::ToUInt32(value.c_str(), 0, 0); - } - if (port == UINT16_MAX) - port = GetNextAvailablePort(); - - // Spawn a new thread to accept the port that gets bound after - // binding to port 0 (zero). - - // ignore the hostname send from the remote end, just use the ip address - // that we're currently communicating with as the hostname - - // Spawn a debugserver and try to get the port it listens to. - ProcessLaunchInfo debugserver_launch_info; - if (hostname.empty()) - hostname = "127.0.0.1"; - if (log) - log->Printf("Launching debugserver with: %s:%u...\n", hostname.c_str(), port); - - // Do not run in a new session so that it can not linger after the - // platform closes. - debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); - debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); - - std::string platform_scheme; - std::string platform_ip; - int platform_port; - std::string platform_path; - bool ok = UriParser::Parse(GetConnection()->GetURI().c_str(), platform_scheme, platform_ip, platform_port, platform_path); - assert(ok); - Error error = StartDebugserverProcess ( - platform_ip.c_str(), - port, - debugserver_launch_info, - port); - - lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); - - - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) - { - Mutex::Locker locker (m_spawned_pids_mutex); - m_spawned_pids.insert(debugserver_pid); - if (port > 0) - AssociatePortWithProcess(port, debugserver_pid); - } - else - { - if (port > 0) - FreePort (port); - } - - if (error.Success()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid); - - char response[256]; - const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); - assert (response_len < (int)sizeof(response)); - PacketResult packet_result = SendPacketNoLock (response, response_len); - - if (packet_result != PacketResult::Success) - { - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) - ::kill (debugserver_pid, SIGINT); - } - return packet_result; - } - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ()); - } - } - return SendErrorResponse (9); -#endif -} - -bool -GDBRemoteCommunicationServer::KillSpawnedProcess (lldb::pid_t pid) -{ - // make sure we know about this process - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - return false; - } - - // first try a SIGTERM (standard kill) - Host::Kill (pid, SIGTERM); - - // check if that worked - for (size_t i=0; i<10; ++i) - { - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - { - // it is now killed - return true; - } - } - usleep (10000); - } - - // check one more time after the final usleep - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - return true; - } - - // the launched process still lives. Now try killing it again, - // this time with an unblockable signal. - Host::Kill (pid, SIGKILL); - - for (size_t i=0; i<10; ++i) - { - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - { - // it is now killed - return true; - } - } - usleep (10000); - } - - // check one more time after the final usleep - // Scope for locker - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - return true; - } - - // no luck - the process still lives - return false; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); - - lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); - - // verify that we know anything about this pid. - // Scope for locker - { - Mutex::Locker locker (m_spawned_pids_mutex); - if (m_spawned_pids.find(pid) == m_spawned_pids.end()) - { - // not a pid we know about - return SendErrorResponse (10); - } - } - - // go ahead and attempt to kill the spawned process - if (KillSpawnedProcess (pid)) - return SendOKResponse (); - else - return SendErrorResponse (11); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_k (StringExtractorGDBRemote &packet) -{ - // ignore for now if we're lldb_platform - if (m_is_platform) - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - - // shutdown all spawned processes - std::set<lldb::pid_t> spawned_pids_copy; - - // copy pids - { - Mutex::Locker locker (m_spawned_pids_mutex); - spawned_pids_copy.insert (m_spawned_pids.begin (), m_spawned_pids.end ()); - } - - // nuke the spawned processes - for (auto it = spawned_pids_copy.begin (); it != spawned_pids_copy.end (); ++it) - { - lldb::pid_t spawned_pid = *it; - if (!KillSpawnedProcess (spawned_pid)) - { - fprintf (stderr, "%s: failed to kill spawned pid %" PRIu64 ", ignoring.\n", __FUNCTION__, spawned_pid); - } - } - - FlushInferiorOutput (); - - // No OK response for kill packet. - // return SendOKResponse (); - return PacketResult::Success; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &packet) -{ - if (m_process_launch_error.Success()) - return SendOKResponse(); - StreamString response; - response.PutChar('E'); - response.PutCString(m_process_launch_error.AsCString("<unknown error>")); - return SendPacketNoLock (response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QEnvironment:")); - const uint32_t bytes_left = packet.GetBytesLeft(); - if (bytes_left > 0) - { - m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek()); - return SendOKResponse (); - } - return SendErrorResponse (12); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QLaunchArch (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QLaunchArch:")); - const uint32_t bytes_left = packet.GetBytesLeft(); - if (bytes_left > 0) - { - const char* arch_triple = packet.Peek(); - ArchSpec arch_spec(arch_triple,NULL); - m_process_launch_info.SetArchitecture(arch_spec); - return SendOKResponse(); - } - return SendErrorResponse(13); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QSetDisableASLR (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QSetDisableASLR:")); - if (packet.GetU32(0)) - m_process_launch_info.GetFlags().Set (eLaunchFlagDisableASLR); - else - m_process_launch_info.GetFlags().Clear (eLaunchFlagDisableASLR); - return SendOKResponse (); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QSetWorkingDir:")); - std::string path; - packet.GetHexByteString(path); - if (m_is_platform) - { -#ifdef _WIN32 - // Not implemented on Windows - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_QSetWorkingDir unimplemented"); -#else - // If this packet is sent to a platform, then change the current working directory - if (::chdir(path.c_str()) != 0) - return SendErrorResponse(errno); -#endif - } - else - { - m_process_launch_info.SwapWorkingDirectory (path); - } - return SendOKResponse (); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qGetWorkingDir (StringExtractorGDBRemote &packet) -{ - StreamString response; - - if (m_is_platform) - { - // If this packet is sent to a platform, then change the current working directory - char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) == NULL) - { - return SendErrorResponse(errno); - } - else - { - response.PutBytesAsRawHex8(cwd, strlen(cwd)); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - } - else - { - const char *working_dir = m_process_launch_info.GetWorkingDirectory(); - if (working_dir && working_dir[0]) - { - response.PutBytesAsRawHex8(working_dir, strlen(working_dir)); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - else - { - return SendErrorResponse(14); - } - } -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QSetSTDIN:")); - FileAction file_action; - std::string path; - packet.GetHexByteString(path); - const bool read = false; - const bool write = true; - if (file_action.Open(STDIN_FILENO, path.c_str(), read, write)) - { - m_process_launch_info.AppendFileAction(file_action); - return SendOKResponse (); - } - return SendErrorResponse (15); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QSetSTDOUT:")); - FileAction file_action; - std::string path; - packet.GetHexByteString(path); - const bool read = true; - const bool write = false; - if (file_action.Open(STDOUT_FILENO, path.c_str(), read, write)) - { - m_process_launch_info.AppendFileAction(file_action); - return SendOKResponse (); - } - return SendErrorResponse (16); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QSetSTDERR:")); - FileAction file_action; - std::string path; - packet.GetHexByteString(path); - const bool read = true; - const bool write = false; - if (file_action.Open(STDERR_FILENO, path.c_str(), read, write)) - { - m_process_launch_info.AppendFileAction(file_action); - return SendOKResponse (); - } - return SendErrorResponse (17); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_C (StringExtractorGDBRemote &packet) -{ - if (!IsGdbServer ()) - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_THREAD)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s called", __FUNCTION__); - - // Ensure we have a native process. - if (!m_debugged_process_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s no debugged process shared pointer", __FUNCTION__); - return SendErrorResponse (0x36); - } - - // Pull out the signal number. - packet.SetFilePos (::strlen ("C")); - if (packet.GetBytesLeft () < 1) - { - // Shouldn't be using a C without a signal. - return SendIllFormedResponse (packet, "C packet specified without signal."); - } - const uint32_t signo = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); - if (signo == std::numeric_limits<uint32_t>::max ()) - return SendIllFormedResponse (packet, "failed to parse signal number"); - - // Handle optional continue address. - if (packet.GetBytesLeft () > 0) - { - // FIXME add continue at address support for $C{signo}[;{continue-address}]. - if (*packet.Peek () == ';') - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - else - return SendIllFormedResponse (packet, "unexpected content after $C{signal-number}"); - } - - lldb_private::ResumeActionList resume_actions (StateType::eStateRunning, 0); - Error error; - - // We have two branches: what to do if a continue thread is specified (in which case we target - // sending the signal to that thread), or when we don't have a continue thread set (in which - // case we send a signal to the process). - - // TODO discuss with Greg Clayton, make sure this makes sense. - - lldb::tid_t signal_tid = GetContinueThreadID (); - if (signal_tid != LLDB_INVALID_THREAD_ID) - { - // The resume action for the continue thread (or all threads if a continue thread is not set). - lldb_private::ResumeAction action = { GetContinueThreadID (), StateType::eStateRunning, static_cast<int> (signo) }; - - // Add the action for the continue thread (or all threads when the continue thread isn't present). - resume_actions.Append (action); - } - else - { - // Send the signal to the process since we weren't targeting a specific continue thread with the signal. - error = m_debugged_process_sp->Signal (signo); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to send signal for process %" PRIu64 ": %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - - return SendErrorResponse (0x52); - } - } - - // Resume the threads. - error = m_debugged_process_sp->Resume (resume_actions); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to resume threads for process %" PRIu64 ": %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - - return SendErrorResponse (0x38); - } - - // Don't send an "OK" packet; response is the stopped/exited message. - return PacketResult::Success; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_c (StringExtractorGDBRemote &packet, bool skip_file_pos_adjustment) -{ - if (!IsGdbServer ()) - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_THREAD)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s called", __FUNCTION__); - - // We reuse this method in vCont - don't double adjust the file position. - if (!skip_file_pos_adjustment) - packet.SetFilePos (::strlen ("c")); - - // For now just support all continue. - const bool has_continue_address = (packet.GetBytesLeft () > 0); - if (has_continue_address) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s not implemented for c{address} variant [%s remains]", __FUNCTION__, packet.Peek ()); - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - } - - // Ensure we have a native process. - if (!m_debugged_process_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s no debugged process shared pointer", __FUNCTION__); - return SendErrorResponse (0x36); - } - - // Build the ResumeActionList - lldb_private::ResumeActionList actions (StateType::eStateRunning, 0); - - Error error = m_debugged_process_sp->Resume (actions); - if (error.Fail ()) - { - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s c failed for process %" PRIu64 ": %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - } - return SendErrorResponse (GDBRemoteServerError::eErrorResume); - } - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s continued process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID ()); - - // No response required from continue. - return PacketResult::Success; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vCont_actions (StringExtractorGDBRemote &packet) -{ - if (!IsGdbServer ()) - { - // only llgs supports $vCont. - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - } - - StreamString response; - response.Printf("vCont;c;C;s;S"); - - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vCont (StringExtractorGDBRemote &packet) -{ - if (!IsGdbServer ()) - { - // only llgs supports $vCont - return SendUnimplementedResponse (packet.GetStringRef().c_str()); - } - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s handling vCont packet", __FUNCTION__); - - packet.SetFilePos (::strlen ("vCont")); - - // Check if this is all continue (no options or ";c"). - if (!packet.GetBytesLeft () || (::strcmp (packet.Peek (), ";c") == 0)) - { - // Move the packet past the ";c". - if (packet.GetBytesLeft ()) - packet.SetFilePos (packet.GetFilePos () + ::strlen (";c")); - - const bool skip_file_pos_adjustment = true; - return Handle_c (packet, skip_file_pos_adjustment); - } - else if (::strcmp (packet.Peek (), ";s") == 0) - { - // Move past the ';', then do a simple 's'. - packet.SetFilePos (packet.GetFilePos () + 1); - return Handle_s (packet); - } - - // Ensure we have a native process. - if (!m_debugged_process_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s no debugged process shared pointer", __FUNCTION__); - return SendErrorResponse (0x36); - } - - ResumeActionList thread_actions; - - while (packet.GetBytesLeft () && *packet.Peek () == ';') - { - // Skip the semi-colon. - packet.GetChar (); - - // Build up the thread action. - ResumeAction thread_action; - thread_action.tid = LLDB_INVALID_THREAD_ID; - thread_action.state = eStateInvalid; - thread_action.signal = 0; - - const char action = packet.GetChar (); - switch (action) - { - case 'C': - thread_action.signal = packet.GetHexMaxU32 (false, 0); - if (thread_action.signal == 0) - return SendIllFormedResponse (packet, "Could not parse signal in vCont packet C action"); - // Fall through to next case... - - case 'c': - // Continue - thread_action.state = eStateRunning; - break; - - case 'S': - thread_action.signal = packet.GetHexMaxU32 (false, 0); - if (thread_action.signal == 0) - return SendIllFormedResponse (packet, "Could not parse signal in vCont packet S action"); - // Fall through to next case... - - case 's': - // Step - thread_action.state = eStateStepping; - break; - - default: - return SendIllFormedResponse (packet, "Unsupported vCont action"); - break; - } - - // Parse out optional :{thread-id} value. - if (packet.GetBytesLeft () && (*packet.Peek () == ':')) - { - // Consume the separator. - packet.GetChar (); - - thread_action.tid = packet.GetHexMaxU32 (false, LLDB_INVALID_THREAD_ID); - if (thread_action.tid == LLDB_INVALID_THREAD_ID) - return SendIllFormedResponse (packet, "Could not parse thread number in vCont packet"); - } - - thread_actions.Append (thread_action); - } - - Error error = m_debugged_process_sp->Resume (thread_actions); - if (error.Fail ()) - { - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s vCont failed for process %" PRIu64 ": %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - } - return SendErrorResponse (GDBRemoteServerError::eErrorResume); - } - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s continued process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID ()); - - // No response required from vCont. - return PacketResult::Success; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote &packet) -{ - // Send response first before changing m_send_acks to we ack this packet - PacketResult packet_result = SendOKResponse (); - m_send_acks = false; - return packet_result; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("qPlatform_mkdir:")); - mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); - if (packet.GetChar() == ',') - { - std::string path; - packet.GetHexByteString(path); - Error error = FileSystem::MakeDirectory(path.c_str(), mode); - if (error.Success()) - return SendPacketNoLock ("OK", 2); - else - return SendErrorResponse(error.GetError()); - } - return SendErrorResponse(20); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qPlatform_chmod (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("qPlatform_chmod:")); - - mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); - if (packet.GetChar() == ',') - { - std::string path; - packet.GetHexByteString(path); - Error error = FileSystem::SetFilePermissions(path.c_str(), mode); - if (error.Success()) - return SendPacketNoLock ("OK", 2); - else - return SendErrorResponse(error.GetError()); - } - return SendErrorResponse(19); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:open:")); - std::string path; - packet.GetHexByteStringTerminatedBy(path,','); - if (!path.empty()) - { - if (packet.GetChar() == ',') - { - uint32_t flags = packet.GetHexMaxU32(false, 0); - if (packet.GetChar() == ',') - { - mode_t mode = packet.GetHexMaxU32(false, 0600); - Error error; - int fd = ::open (path.c_str(), flags, mode); - const int save_errno = fd == -1 ? errno : 0; - StreamString response; - response.PutChar('F'); - response.Printf("%i", fd); - if (save_errno) - response.Printf(",%i", save_errno); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - } - } - return SendErrorResponse(18); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_Close (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:close:")); - int fd = packet.GetS32(-1); - Error error; - int err = -1; - int save_errno = 0; - if (fd >= 0) - { - err = close(fd); - save_errno = err == -1 ? errno : 0; - } - else - { - save_errno = EINVAL; - } - StreamString response; - response.PutChar('F'); - response.Printf("%i", err); - if (save_errno) - response.Printf(",%i", save_errno); - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_pRead (StringExtractorGDBRemote &packet) -{ -#ifdef _WIN32 - // Not implemented on Windows - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_pRead() unimplemented"); -#else - StreamGDBRemote response; - packet.SetFilePos(::strlen("vFile:pread:")); - int fd = packet.GetS32(-1); - if (packet.GetChar() == ',') - { - uint64_t count = packet.GetU64(UINT64_MAX); - if (packet.GetChar() == ',') - { - uint64_t offset = packet.GetU64(UINT32_MAX); - if (count == UINT64_MAX) - { - response.Printf("F-1:%i", EINVAL); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - - std::string buffer(count, 0); - const ssize_t bytes_read = ::pread (fd, &buffer[0], buffer.size(), offset); - const int save_errno = bytes_read == -1 ? errno : 0; - response.PutChar('F'); - response.Printf("%zi", bytes_read); - if (save_errno) - response.Printf(",%i", save_errno); - else - { - response.PutChar(';'); - response.PutEscapedBytes(&buffer[0], bytes_read); - } - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - } - return SendErrorResponse(21); - -#endif -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &packet) -{ -#ifdef _WIN32 - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_pWrite() unimplemented"); -#else - packet.SetFilePos(::strlen("vFile:pwrite:")); - - StreamGDBRemote response; - response.PutChar('F'); - - int fd = packet.GetU32(UINT32_MAX); - if (packet.GetChar() == ',') - { - off_t offset = packet.GetU64(UINT32_MAX); - if (packet.GetChar() == ',') - { - std::string buffer; - if (packet.GetEscapedBinaryData(buffer)) - { - const ssize_t bytes_written = ::pwrite (fd, buffer.data(), buffer.size(), offset); - const int save_errno = bytes_written == -1 ? errno : 0; - response.Printf("%zi", bytes_written); - if (save_errno) - response.Printf(",%i", save_errno); - } - else - { - response.Printf ("-1,%i", EINVAL); - } - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - } - return SendErrorResponse(27); -#endif -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_Size (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:size:")); - std::string path; - packet.GetHexByteString(path); - if (!path.empty()) - { - lldb::user_id_t retcode = FileSystem::GetFileSize(FileSpec(path.c_str(), false)); - StreamString response; - response.PutChar('F'); - response.PutHex64(retcode); - if (retcode == UINT64_MAX) - { - response.PutChar(','); - response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode() - } - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - return SendErrorResponse(22); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_Mode (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:mode:")); - std::string path; - packet.GetHexByteString(path); - if (!path.empty()) - { - Error error; - const uint32_t mode = File::GetPermissions(path.c_str(), error); - StreamString response; - response.Printf("F%u", mode); - if (mode == 0 || error.Fail()) - response.Printf(",%i", (int)error.GetError()); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - return SendErrorResponse(23); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_Exists (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:exists:")); - std::string path; - packet.GetHexByteString(path); - if (!path.empty()) - { - bool retcode = FileSystem::GetFileExists(FileSpec(path.c_str(), false)); - StreamString response; - response.PutChar('F'); - response.PutChar(','); - if (retcode) - response.PutChar('1'); - else - response.PutChar('0'); - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - return SendErrorResponse(24); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_symlink (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:symlink:")); - std::string dst, src; - packet.GetHexByteStringTerminatedBy(dst, ','); - packet.GetChar(); // Skip ',' char - packet.GetHexByteString(src); - Error error = FileSystem::Symlink(src.c_str(), dst.c_str()); - StreamString response; - response.Printf("F%u,%u", error.GetError(), error.GetError()); - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_unlink (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:unlink:")); - std::string path; - packet.GetHexByteString(path); - Error error = FileSystem::Unlink(path.c_str()); - StreamString response; - response.Printf("F%u,%u", error.GetError(), error.GetError()); - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qPlatform_shell (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("qPlatform_shell:")); - std::string path; - std::string working_dir; - packet.GetHexByteStringTerminatedBy(path,','); - if (!path.empty()) - { - if (packet.GetChar() == ',') - { - // FIXME: add timeout to qPlatform_shell packet - // uint32_t timeout = packet.GetHexMaxU32(false, 32); - uint32_t timeout = 10; - if (packet.GetChar() == ',') - packet.GetHexByteString(working_dir); - int status, signo; - std::string output; - Error err = Host::RunShellCommand(path.c_str(), - working_dir.empty() ? NULL : working_dir.c_str(), - &status, &signo, &output, timeout); - StreamGDBRemote response; - if (err.Fail()) - { - response.PutCString("F,"); - response.PutHex32(UINT32_MAX); - } - else - { - response.PutCString("F,"); - response.PutHex32(status); - response.PutChar(','); - response.PutHex32(signo); - response.PutChar(','); - response.PutEscapedBytes(output.c_str(), output.size()); - } - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - } - return SendErrorResponse(24); -} - -void -GDBRemoteCommunicationServer::SetCurrentThreadID (lldb::tid_t tid) -{ - assert (IsGdbServer () && "SetCurrentThreadID() called when not GdbServer code"); - - Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s setting current thread id to %" PRIu64, __FUNCTION__, tid); - - m_current_tid = tid; - if (m_debugged_process_sp) - m_debugged_process_sp->SetCurrentThreadID (m_current_tid); -} - -void -GDBRemoteCommunicationServer::SetContinueThreadID (lldb::tid_t tid) -{ - assert (IsGdbServer () && "SetContinueThreadID() called when not GdbServer code"); - - Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s setting continue thread id to %" PRIu64, __FUNCTION__, tid); - - m_continue_tid = tid; -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_stop_reason (StringExtractorGDBRemote &packet) -{ - // Handle the $? gdbremote command. - if (!IsGdbServer ()) - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_stop_reason() unimplemented"); - - // If no process, indicate error - if (!m_debugged_process_sp) - return SendErrorResponse (02); - - return SendStopReasonForState (m_debugged_process_sp->GetState (), true); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::SendStopReasonForState (lldb::StateType process_state, bool flush_on_exit) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - switch (process_state) - { - case eStateAttaching: - case eStateLaunching: - case eStateRunning: - case eStateStepping: - case eStateDetached: - // NOTE: gdb protocol doc looks like it should return $OK - // when everything is running (i.e. no stopped result). - return PacketResult::Success; // Ignore - - case eStateSuspended: - case eStateStopped: - case eStateCrashed: - { - lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID (); - // Make sure we set the current thread so g and p packets return - // the data the gdb will expect. - SetCurrentThreadID (tid); - return SendStopReplyPacketForThread (tid); - } - - case eStateInvalid: - case eStateUnloaded: - case eStateExited: - if (flush_on_exit) - FlushInferiorOutput (); - return SendWResponse(m_debugged_process_sp.get()); - - default: - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 ", current state reporting not handled: %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - StateAsCString (process_state)); - } - break; - } - - return SendErrorResponse (0); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_Stat (StringExtractorGDBRemote &packet) -{ - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_vFile_Stat() unimplemented"); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_vFile_MD5 (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen("vFile:MD5:")); - std::string path; - packet.GetHexByteString(path); - if (!path.empty()) - { - uint64_t a,b; - StreamGDBRemote response; - if (FileSystem::CalculateMD5(FileSpec(path.c_str(), false), a, b) == false) - { - response.PutCString("F,"); - response.PutCString("x"); - } - else - { - response.PutCString("F,"); - response.PutHex64(a); - response.PutHex64(b); - } - return SendPacketNoLock(response.GetData(), response.GetSize()); - } - return SendErrorResponse(25); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qRegisterInfo (StringExtractorGDBRemote &packet) -{ - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_qRegisterInfo() unimplemented"); - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - return SendErrorResponse (68); - - // Ensure we have a thread. - NativeThreadProtocolSP thread_sp (m_debugged_process_sp->GetThreadAtIndex (0)); - if (!thread_sp) - return SendErrorResponse (69); - - // Get the register context for the first thread. - NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); - if (!reg_context_sp) - return SendErrorResponse (69); - - // Parse out the register number from the request. - packet.SetFilePos (strlen("qRegisterInfo")); - const uint32_t reg_index = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); - if (reg_index == std::numeric_limits<uint32_t>::max ()) - return SendErrorResponse (69); - - // Return the end of registers response if we've iterated one past the end of the register set. - if (reg_index >= reg_context_sp->GetUserRegisterCount ()) - return SendErrorResponse (69); - - const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); - if (!reg_info) - return SendErrorResponse (69); - - // Build the reginfos response. - StreamGDBRemote response; - - response.PutCString ("name:"); - response.PutCString (reg_info->name); - response.PutChar (';'); - - if (reg_info->alt_name && reg_info->alt_name[0]) - { - response.PutCString ("alt-name:"); - response.PutCString (reg_info->alt_name); - response.PutChar (';'); - } - - response.Printf ("bitsize:%" PRIu32 ";offset:%" PRIu32 ";", reg_info->byte_size * 8, reg_info->byte_offset); - - switch (reg_info->encoding) - { - case eEncodingUint: response.PutCString ("encoding:uint;"); break; - case eEncodingSint: response.PutCString ("encoding:sint;"); break; - case eEncodingIEEE754: response.PutCString ("encoding:ieee754;"); break; - case eEncodingVector: response.PutCString ("encoding:vector;"); break; - default: break; - } - - switch (reg_info->format) - { - case eFormatBinary: response.PutCString ("format:binary;"); break; - case eFormatDecimal: response.PutCString ("format:decimal;"); break; - case eFormatHex: response.PutCString ("format:hex;"); break; - case eFormatFloat: response.PutCString ("format:float;"); break; - case eFormatVectorOfSInt8: response.PutCString ("format:vector-sint8;"); break; - case eFormatVectorOfUInt8: response.PutCString ("format:vector-uint8;"); break; - case eFormatVectorOfSInt16: response.PutCString ("format:vector-sint16;"); break; - case eFormatVectorOfUInt16: response.PutCString ("format:vector-uint16;"); break; - case eFormatVectorOfSInt32: response.PutCString ("format:vector-sint32;"); break; - case eFormatVectorOfUInt32: response.PutCString ("format:vector-uint32;"); break; - case eFormatVectorOfFloat32: response.PutCString ("format:vector-float32;"); break; - case eFormatVectorOfUInt128: response.PutCString ("format:vector-uint128;"); break; - default: break; - }; - - const char *const register_set_name = reg_context_sp->GetRegisterSetNameForRegisterAtIndex(reg_index); - if (register_set_name) - { - response.PutCString ("set:"); - response.PutCString (register_set_name); - response.PutChar (';'); - } - - if (reg_info->kinds[RegisterKind::eRegisterKindGCC] != LLDB_INVALID_REGNUM) - response.Printf ("gcc:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindGCC]); - - if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM) - response.Printf ("dwarf:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindDWARF]); - - switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) - { - case LLDB_REGNUM_GENERIC_PC: response.PutCString("generic:pc;"); break; - case LLDB_REGNUM_GENERIC_SP: response.PutCString("generic:sp;"); break; - case LLDB_REGNUM_GENERIC_FP: response.PutCString("generic:fp;"); break; - case LLDB_REGNUM_GENERIC_RA: response.PutCString("generic:ra;"); break; - case LLDB_REGNUM_GENERIC_FLAGS: response.PutCString("generic:flags;"); break; - case LLDB_REGNUM_GENERIC_ARG1: response.PutCString("generic:arg1;"); break; - case LLDB_REGNUM_GENERIC_ARG2: response.PutCString("generic:arg2;"); break; - case LLDB_REGNUM_GENERIC_ARG3: response.PutCString("generic:arg3;"); break; - case LLDB_REGNUM_GENERIC_ARG4: response.PutCString("generic:arg4;"); break; - case LLDB_REGNUM_GENERIC_ARG5: response.PutCString("generic:arg5;"); break; - case LLDB_REGNUM_GENERIC_ARG6: response.PutCString("generic:arg6;"); break; - case LLDB_REGNUM_GENERIC_ARG7: response.PutCString("generic:arg7;"); break; - case LLDB_REGNUM_GENERIC_ARG8: response.PutCString("generic:arg8;"); break; - default: break; - } - - if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) - { - response.PutCString ("container-regs:"); - int i = 0; - for (const uint32_t *reg_num = reg_info->value_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) - { - if (i > 0) - response.PutChar (','); - response.Printf ("%" PRIx32, *reg_num); - } - response.PutChar (';'); - } - - if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) - { - response.PutCString ("invalidate-regs:"); - int i = 0; - for (const uint32_t *reg_num = reg_info->invalidate_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) - { - if (i > 0) - response.PutChar (','); - response.Printf ("%" PRIx32, *reg_num); - } - response.PutChar (';'); - } - - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qfThreadInfo (StringExtractorGDBRemote &packet) -{ - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_qfThreadInfo() unimplemented"); - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() no process (%s), returning OK", __FUNCTION__, m_debugged_process_sp ? "invalid process id" : "null m_debugged_process_sp"); - return SendOKResponse (); - } - - StreamGDBRemote response; - response.PutChar ('m'); - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() starting thread iteration", __FUNCTION__); - - NativeThreadProtocolSP thread_sp; - uint32_t thread_index; - for (thread_index = 0, thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index); - thread_sp; - ++thread_index, thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() iterated thread %" PRIu32 "(%s, tid=0x%" PRIx64 ")", __FUNCTION__, thread_index, thread_sp ? "is not null" : "null", thread_sp ? thread_sp->GetID () : LLDB_INVALID_THREAD_ID); - if (thread_index > 0) - response.PutChar(','); - response.Printf ("%" PRIx64, thread_sp->GetID ()); - } - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() finished thread iteration", __FUNCTION__); - - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_qsThreadInfo (StringExtractorGDBRemote &packet) -{ - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("GDBRemoteCommunicationServer::Handle_qsThreadInfo() unimplemented"); - - // FIXME for now we return the full thread list in the initial packet and always do nothing here. - return SendPacketNoLock ("l", 1); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_p (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("GDBRemoteCommunicationServer::Handle_p() unimplemented"); - - // Parse out the register number from the request. - packet.SetFilePos (strlen("p")); - const uint32_t reg_index = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); - if (reg_index == std::numeric_limits<uint32_t>::max ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, could not parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); - return SendErrorResponse (0x15); - } - - // Get the thread to use. - NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); - if (!thread_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no thread available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Get the thread's register context. - NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); - if (!reg_context_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); - return SendErrorResponse (0x15); - } - - // Return the end of registers response if we've iterated one past the end of the register set. - if (reg_index >= reg_context_sp->GetUserRegisterCount ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, requested register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount ()); - return SendErrorResponse (0x15); - } - - const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); - if (!reg_info) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, requested register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); - return SendErrorResponse (0x15); - } - - // Build the reginfos response. - StreamGDBRemote response; - - // Retrieve the value - RegisterValue reg_value; - Error error = reg_context_sp->ReadRegister (reg_info, reg_value); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, read of requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString ()); - return SendErrorResponse (0x15); - } - - const uint8_t *const data = reinterpret_cast<const uint8_t*> (reg_value.GetBytes ()); - if (!data) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to get data bytes from requested register %" PRIu32, __FUNCTION__, reg_index); - return SendErrorResponse (0x15); - } - - // FIXME flip as needed to get data in big/little endian format for this host. - for (uint32_t i = 0; i < reg_value.GetByteSize (); ++i) - response.PutHex8 (data[i]); - - return SendPacketNoLock (response.GetData (), response.GetSize ()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_P (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("GDBRemoteCommunicationServer::Handle_P() unimplemented"); - - // Ensure there is more content. - if (packet.GetBytesLeft () < 1) - return SendIllFormedResponse (packet, "Empty P packet"); - - // Parse out the register number from the request. - packet.SetFilePos (strlen("P")); - const uint32_t reg_index = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); - if (reg_index == std::numeric_limits<uint32_t>::max ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, could not parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); - return SendErrorResponse (0x29); - } - - // Note debugserver would send an E30 here. - if ((packet.GetBytesLeft () < 1) || (packet.GetChar () != '=')) - return SendIllFormedResponse (packet, "P packet missing '=' char after register number"); - - // Get process architecture. - ArchSpec process_arch; - if (!m_debugged_process_sp || !m_debugged_process_sp->GetArchitecture (process_arch)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to retrieve inferior architecture", __FUNCTION__); - return SendErrorResponse (0x49); - } - - // Parse out the value. - uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register - size_t reg_size = packet.GetHexBytesAvail (reg_bytes, sizeof(reg_bytes)); - - // Get the thread to use. - NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); - if (!thread_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no thread available (thread index 0)", __FUNCTION__); - return SendErrorResponse (0x28); - } - - // Get the thread's register context. - NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); - if (!reg_context_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); - return SendErrorResponse (0x15); - } - - const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex (reg_index); - if (!reg_info) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, requested register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); - return SendErrorResponse (0x48); - } - - // Return the end of registers response if we've iterated one past the end of the register set. - if (reg_index >= reg_context_sp->GetUserRegisterCount ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, requested register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount ()); - return SendErrorResponse (0x47); - } - - if (reg_size != reg_info->byte_size) - { - return SendIllFormedResponse (packet, "P packet register size is incorrect"); - } - - // Build the reginfos response. - StreamGDBRemote response; - - RegisterValue reg_value (reg_bytes, reg_size, process_arch.GetByteOrder ()); - Error error = reg_context_sp->WriteRegister (reg_info, reg_value); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, write of requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString ()); - return SendErrorResponse (0x32); - } - - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_H (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_H() unimplemented"); - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Parse out which variant of $H is requested. - packet.SetFilePos (strlen("H")); - if (packet.GetBytesLeft () < 1) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, H command missing {g,c} variant", __FUNCTION__); - return SendIllFormedResponse (packet, "H command missing {g,c} variant"); - } - - const char h_variant = packet.GetChar (); - switch (h_variant) - { - case 'g': - break; - - case 'c': - break; - - default: - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, invalid $H variant %c", __FUNCTION__, h_variant); - return SendIllFormedResponse (packet, "H variant unsupported, should be c or g"); - } - - // Parse out the thread number. - // FIXME return a parse success/fail value. All values are valid here. - const lldb::tid_t tid = packet.GetHexMaxU64 (false, std::numeric_limits<lldb::tid_t>::max ()); - - // Ensure we have the given thread when not specifying -1 (all threads) or 0 (any thread). - if (tid != LLDB_INVALID_THREAD_ID && tid != 0) - { - NativeThreadProtocolSP thread_sp (m_debugged_process_sp->GetThreadByID (tid)); - if (!thread_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, tid %" PRIu64 " not found", __FUNCTION__, tid); - return SendErrorResponse (0x15); - } - } - - // Now switch the given thread type. - switch (h_variant) - { - case 'g': - SetCurrentThreadID (tid); - break; - - case 'c': - SetContinueThreadID (tid); - break; - - default: - assert (false && "unsupported $H variant - shouldn't get here"); - return SendIllFormedResponse (packet, "H variant unsupported, should be c or g"); - } - - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_I (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // Ensure we're llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse("GDBRemoteCommunicationServer::Handle_I() unimplemented"); - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - packet.SetFilePos (::strlen("I")); - char tmp[4096]; - for (;;) - { - size_t read = packet.GetHexBytesAvail(tmp, sizeof(tmp)); - if (read == 0) - { - break; - } - // write directly to stdin *this might block if stdin buffer is full* - // TODO: enqueue this block in circular buffer and send window size to remote host - ConnectionStatus status; - Error error; - m_stdio_communication.Write(tmp, read, status, &error); - if (error.Fail()) - { - return SendErrorResponse (0x15); - } - } - - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_interrupt (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); - - // Ensure we're llgs. - if (!IsGdbServer()) - { - // Only supported on llgs - return SendUnimplementedResponse (""); - } - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Interrupt the process. - Error error = m_debugged_process_sp->Interrupt (); - if (error.Fail ()) - { - if (log) - { - log->Printf ("GDBRemoteCommunicationServer::%s failed for process %" PRIu64 ": %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - } - return SendErrorResponse (GDBRemoteServerError::eErrorResume); - } - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s stopped process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID ()); - - // No response required from stop all. - return PacketResult::Success; -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_m (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // Ensure we're llgs. - if (!IsGdbServer()) - { - // Only supported on llgs - return SendUnimplementedResponse (""); - } - - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Parse out the memory address. - packet.SetFilePos (strlen("m")); - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short m packet"); - - // Read the address. Punting on validation. - // FIXME replace with Hex U64 read with no default value that fails on failed read. - const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); - - // Validate comma. - if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) - return SendIllFormedResponse(packet, "Comma sep missing in m packet"); - - // Get # bytes to read. - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Length missing in m packet"); - - const uint64_t byte_count = packet.GetHexMaxU64(false, 0); - if (byte_count == 0) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s nothing to read: zero-length packet", __FUNCTION__); - return PacketResult::Success; - } - - // Allocate the response buffer. - std::string buf(byte_count, '\0'); - if (buf.empty()) - return SendErrorResponse (0x78); - - - // Retrieve the process memory. - lldb::addr_t bytes_read = 0; - lldb_private::Error error = m_debugged_process_sp->ReadMemory (read_addr, &buf[0], byte_count, bytes_read); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to read. Error: %s", __FUNCTION__, m_debugged_process_sp->GetID (), read_addr, error.AsCString ()); - return SendErrorResponse (0x08); - } - - if (bytes_read == 0) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " mem 0x%" PRIx64 ": read %" PRIu64 " of %" PRIu64 " requested bytes", __FUNCTION__, m_debugged_process_sp->GetID (), read_addr, bytes_read, byte_count); - return SendErrorResponse (0x08); - } - - StreamGDBRemote response; - for (lldb::addr_t i = 0; i < bytes_read; ++i) - response.PutHex8(buf[i]); - - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServer::Handle_QSetDetachOnError (StringExtractorGDBRemote &packet) -{ - packet.SetFilePos(::strlen ("QSetDetachOnError:")); - if (packet.GetU32(0)) - m_process_launch_info.GetFlags().Set (eLaunchFlagDetachOnError); - else - m_process_launch_info.GetFlags().Clear (eLaunchFlagDetachOnError); - return SendOKResponse (); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_M (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // Ensure we're llgs. - if (!IsGdbServer()) - { - // Only supported on llgs - return SendUnimplementedResponse (""); - } - - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Parse out the memory address. - packet.SetFilePos (strlen("M")); - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short M packet"); - - // Read the address. Punting on validation. - // FIXME replace with Hex U64 read with no default value that fails on failed read. - const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0); - - // Validate comma. - if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) - return SendIllFormedResponse(packet, "Comma sep missing in M packet"); - - // Get # bytes to read. - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Length missing in M packet"); - - const uint64_t byte_count = packet.GetHexMaxU64(false, 0); - if (byte_count == 0) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s nothing to write: zero-length packet", __FUNCTION__); - return PacketResult::Success; - } - - // Validate colon. - if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':')) - return SendIllFormedResponse(packet, "Comma sep missing in M packet after byte length"); - - // Allocate the conversion buffer. - std::vector<uint8_t> buf(byte_count, 0); - if (buf.empty()) - return SendErrorResponse (0x78); - - // Convert the hex memory write contents to bytes. - StreamGDBRemote response; - const uint64_t convert_count = static_cast<uint64_t> (packet.GetHexBytes (&buf[0], byte_count, 0)); - if (convert_count != byte_count) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " mem 0x%" PRIx64 ": asked to write %" PRIu64 " bytes, but only found %" PRIu64 " to convert.", __FUNCTION__, m_debugged_process_sp->GetID (), write_addr, byte_count, convert_count); - return SendIllFormedResponse (packet, "M content byte length specified did not match hex-encoded content length"); - } - - // Write the process memory. - lldb::addr_t bytes_written = 0; - lldb_private::Error error = m_debugged_process_sp->WriteMemory (write_addr, &buf[0], byte_count, bytes_written); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to write. Error: %s", __FUNCTION__, m_debugged_process_sp->GetID (), write_addr, error.AsCString ()); - return SendErrorResponse (0x09); - } - - if (bytes_written == 0) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " mem 0x%" PRIx64 ": wrote %" PRIu64 " of %" PRIu64 " requested bytes", __FUNCTION__, m_debugged_process_sp->GetID (), write_addr, bytes_written, byte_count); - return SendErrorResponse (0x09); - } - - return SendOKResponse (); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_qMemoryRegionInfoSupported (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse (""); - - // Currently only the NativeProcessProtocol knows if it can handle a qMemoryRegionInfoSupported - // request, but we're not guaranteed to be attached to a process. For now we'll assume the - // client only asks this when a process is being debugged. - - // Ensure we have a process running; otherwise, we can't figure this out - // since we won't have a NativeProcessProtocol. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Test if we can get any region back when asking for the region around NULL. - MemoryRegionInfo region_info; - const Error error = m_debugged_process_sp->GetMemoryRegionInfo (0, region_info); - if (error.Fail ()) - { - // We don't support memory region info collection for this NativeProcessProtocol. - return SendUnimplementedResponse (""); - } - - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_qMemoryRegionInfo (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse (""); - - // Ensure we have a process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Parse out the memory address. - packet.SetFilePos (strlen("qMemoryRegionInfo:")); - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet"); - - // Read the address. Punting on validation. - const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); - - StreamGDBRemote response; - - // Get the memory region info for the target address. - MemoryRegionInfo region_info; - const Error error = m_debugged_process_sp->GetMemoryRegionInfo (read_addr, region_info); - if (error.Fail ()) - { - // Return the error message. - - response.PutCString ("error:"); - response.PutCStringAsRawHex8 (error.AsCString ()); - response.PutChar (';'); - } - else - { - // Range start and size. - response.Printf ("start:%" PRIx64 ";size:%" PRIx64 ";", region_info.GetRange ().GetRangeBase (), region_info.GetRange ().GetByteSize ()); - - // Permissions. - if (region_info.GetReadable () || - region_info.GetWritable () || - region_info.GetExecutable ()) - { - // Write permissions info. - response.PutCString ("permissions:"); - - if (region_info.GetReadable ()) - response.PutChar ('r'); - if (region_info.GetWritable ()) - response.PutChar('w'); - if (region_info.GetExecutable()) - response.PutChar ('x'); - - response.PutChar (';'); - } - } - - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_Z (StringExtractorGDBRemote &packet) -{ - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse (""); - - // Ensure we have a process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Parse out software or hardware breakpoint or watchpoint requested. - packet.SetFilePos (strlen("Z")); - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short Z packet, missing software/hardware specifier"); - - bool want_breakpoint = true; - bool want_hardware = false; - - const GDBStoppointType stoppoint_type = - GDBStoppointType(packet.GetS32 (eStoppointInvalid)); - switch (stoppoint_type) - { - case eBreakpointSoftware: - want_hardware = false; want_breakpoint = true; break; - case eBreakpointHardware: - want_hardware = true; want_breakpoint = true; break; - case eWatchpointWrite: - want_hardware = true; want_breakpoint = false; break; - case eWatchpointRead: - want_hardware = true; want_breakpoint = false; break; - case eWatchpointReadWrite: - want_hardware = true; want_breakpoint = false; break; - case eStoppointInvalid: - return SendIllFormedResponse(packet, "Z packet had invalid software/hardware specifier"); - - } - - if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after stoppoint type"); - - // Parse out the stoppoint address. - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short Z packet, missing address"); - const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); - - if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after address"); - - // Parse out the stoppoint size (i.e. size hint for opcode size). - const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); - if (size == std::numeric_limits<uint32_t>::max ()) - return SendIllFormedResponse(packet, "Malformed Z packet, failed to parse size argument"); - - if (want_breakpoint) - { - // Try to set the breakpoint. - const Error error = m_debugged_process_sp->SetBreakpoint (addr, size, want_hardware); - if (error.Success ()) - return SendOKResponse (); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 - " failed to set breakpoint: %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - return SendErrorResponse (0x09); - } - else - { - uint32_t watch_flags = - stoppoint_type == eWatchpointWrite - ? watch_flags = 0x1 // Write - : watch_flags = 0x3; // ReadWrite - - // Try to set the watchpoint. - const Error error = m_debugged_process_sp->SetWatchpoint ( - addr, size, watch_flags, want_hardware); - if (error.Success ()) - return SendOKResponse (); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 - " failed to set watchpoint: %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - return SendErrorResponse (0x09); - } -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_z (StringExtractorGDBRemote &packet) -{ - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse (""); - - // Ensure we have a process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - // Parse out software or hardware breakpoint or watchpoint requested. - packet.SetFilePos (strlen("z")); - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short z packet, missing software/hardware specifier"); - - bool want_breakpoint = true; - - const GDBStoppointType stoppoint_type = - GDBStoppointType(packet.GetS32 (eStoppointInvalid)); - switch (stoppoint_type) - { - case eBreakpointHardware: want_breakpoint = true; break; - case eBreakpointSoftware: want_breakpoint = true; break; - case eWatchpointWrite: want_breakpoint = false; break; - case eWatchpointRead: want_breakpoint = false; break; - case eWatchpointReadWrite: want_breakpoint = false; break; - default: - return SendIllFormedResponse(packet, "z packet had invalid software/hardware specifier"); - - } - - if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after stoppoint type"); - - // Parse out the stoppoint address. - if (packet.GetBytesLeft() < 1) - return SendIllFormedResponse(packet, "Too short z packet, missing address"); - const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); - - if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') - return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after address"); - - /* - // Parse out the stoppoint size (i.e. size hint for opcode size). - const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); - if (size == std::numeric_limits<uint32_t>::max ()) - return SendIllFormedResponse(packet, "Malformed z packet, failed to parse size argument"); - */ - - if (want_breakpoint) - { - // Try to clear the breakpoint. - const Error error = m_debugged_process_sp->RemoveBreakpoint (addr); - if (error.Success ()) - return SendOKResponse (); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 - " failed to remove breakpoint: %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - return SendErrorResponse (0x09); - } - else - { - // Try to clear the watchpoint. - const Error error = m_debugged_process_sp->RemoveWatchpoint (addr); - if (error.Success ()) - return SendOKResponse (); - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 - " failed to remove watchpoint: %s", - __FUNCTION__, - m_debugged_process_sp->GetID (), - error.AsCString ()); - return SendErrorResponse (0x09); - } -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_s (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_THREAD)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse (""); - - // Ensure we have a process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x32); - } - - // We first try to use a continue thread id. If any one or any all set, use the current thread. - // Bail out if we don't have a thread id. - lldb::tid_t tid = GetContinueThreadID (); - if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) - tid = GetCurrentThreadID (); - if (tid == LLDB_INVALID_THREAD_ID) - return SendErrorResponse (0x33); - - // Double check that we have such a thread. - // TODO investigate: on MacOSX we might need to do an UpdateThreads () here. - NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetThreadByID (tid); - if (!thread_sp || thread_sp->GetID () != tid) - return SendErrorResponse (0x33); - - // Create the step action for the given thread. - lldb_private::ResumeAction action = { tid, eStateStepping, 0 }; - - // Setup the actions list. - lldb_private::ResumeActionList actions; - actions.Append (action); - - // All other threads stop while we're single stepping a thread. - actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); - Error error = m_debugged_process_sp->Resume (actions); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " Resume() failed with error: %s", __FUNCTION__, m_debugged_process_sp->GetID (), tid, error.AsCString ()); - return SendErrorResponse(0x49); - } - - // No response here - the stop or exit will come from the resulting action. - return PacketResult::Success; -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_qSupported (StringExtractorGDBRemote &packet) -{ - StreamGDBRemote response; - - // Features common to lldb-platform and llgs. - uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet size--debugger can always use less - response.Printf ("PacketSize=%x", max_packet_size); - - response.PutCString (";QStartNoAckMode+"); - response.PutCString (";QThreadSuffixSupported+"); - response.PutCString (";QListThreadsInStopReply+"); -#if defined(__linux__) - response.PutCString (";qXfer:auxv:read+"); -#endif - - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_QThreadSuffixSupported (StringExtractorGDBRemote &packet) -{ - m_thread_suffix_supported = true; - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_QListThreadsInStopReply (StringExtractorGDBRemote &packet) -{ - m_list_threads_in_stop_reply = true; - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_qXfer_auxv_read (StringExtractorGDBRemote &packet) -{ - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("only supported for lldb-gdbserver"); - - // *BSD impls should be able to do this too. -#if defined(__linux__) - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // Parse out the offset. - packet.SetFilePos (strlen("qXfer:auxv:read::")); - if (packet.GetBytesLeft () < 1) - return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing offset"); - - const uint64_t auxv_offset = packet.GetHexMaxU64 (false, std::numeric_limits<uint64_t>::max ()); - if (auxv_offset == std::numeric_limits<uint64_t>::max ()) - return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing offset"); - - // Parse out comma. - if (packet.GetBytesLeft () < 1 || packet.GetChar () != ',') - return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing comma after offset"); - - // Parse out the length. - const uint64_t auxv_length = packet.GetHexMaxU64 (false, std::numeric_limits<uint64_t>::max ()); - if (auxv_length == std::numeric_limits<uint64_t>::max ()) - return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing length"); - - // Grab the auxv data if we need it. - if (!m_active_auxv_buffer_sp) - { - // Make sure we have a valid process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x10); - } - - // Grab the auxv data. - m_active_auxv_buffer_sp = Host::GetAuxvData (m_debugged_process_sp->GetID ()); - if (!m_active_auxv_buffer_sp || m_active_auxv_buffer_sp->GetByteSize () == 0) - { - // Hmm, no auxv data, call that an error. - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no auxv data retrieved", __FUNCTION__); - m_active_auxv_buffer_sp.reset (); - return SendErrorResponse (0x11); - } - } - - // FIXME find out if/how I lock the stream here. - - StreamGDBRemote response; - bool done_with_buffer = false; - - if (auxv_offset >= m_active_auxv_buffer_sp->GetByteSize ()) - { - // We have nothing left to send. Mark the buffer as complete. - response.PutChar ('l'); - done_with_buffer = true; - } - else - { - // Figure out how many bytes are available starting at the given offset. - const uint64_t bytes_remaining = m_active_auxv_buffer_sp->GetByteSize () - auxv_offset; - - // Figure out how many bytes we're going to read. - const uint64_t bytes_to_read = (auxv_length > bytes_remaining) ? bytes_remaining : auxv_length; - - // Mark the response type according to whether we're reading the remainder of the auxv data. - if (bytes_to_read >= bytes_remaining) - { - // There will be nothing left to read after this - response.PutChar ('l'); - done_with_buffer = true; - } - else - { - // There will still be bytes to read after this request. - response.PutChar ('m'); - } - - // Now write the data in encoded binary form. - response.PutEscapedBytes (m_active_auxv_buffer_sp->GetBytes () + auxv_offset, bytes_to_read); - } - - if (done_with_buffer) - m_active_auxv_buffer_sp.reset (); - - return SendPacketNoLock(response.GetData(), response.GetSize()); -#else - return SendUnimplementedResponse ("not implemented on this platform"); -#endif -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_QSaveRegisterState (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("only supported for lldb-gdbserver"); - - // Move past packet name. - packet.SetFilePos (strlen ("QSaveRegisterState")); - - // Get the thread to use. - NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); - if (!thread_sp) - { - if (m_thread_suffix_supported) - return SendIllFormedResponse (packet, "No thread specified in QSaveRegisterState packet"); - else - return SendIllFormedResponse (packet, "No thread was is set with the Hg packet"); - } - - // Grab the register context for the thread. - NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); - if (!reg_context_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); - return SendErrorResponse (0x15); - } - - // Save registers to a buffer. - DataBufferSP register_data_sp; - Error error = reg_context_sp->ReadAllRegisterValues (register_data_sp); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " failed to save all register values: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x75); - } - - // Allocate a new save id. - const uint32_t save_id = GetNextSavedRegistersID (); - assert ((m_saved_registers_map.find (save_id) == m_saved_registers_map.end ()) && "GetNextRegisterSaveID() returned an existing register save id"); - - // Save the register data buffer under the save id. - { - Mutex::Locker locker (m_saved_registers_mutex); - m_saved_registers_map[save_id] = register_data_sp; - } - - // Write the response. - StreamGDBRemote response; - response.Printf ("%" PRIu32, save_id); - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_QRestoreRegisterState (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("only supported for lldb-gdbserver"); - - // Parse out save id. - packet.SetFilePos (strlen ("QRestoreRegisterState:")); - if (packet.GetBytesLeft () < 1) - return SendIllFormedResponse (packet, "QRestoreRegisterState packet missing register save id"); - - const uint32_t save_id = packet.GetU32 (0); - if (save_id == 0) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s QRestoreRegisterState packet has malformed save id, expecting decimal uint32_t", __FUNCTION__); - return SendErrorResponse (0x76); - } - - // Get the thread to use. - NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); - if (!thread_sp) - { - if (m_thread_suffix_supported) - return SendIllFormedResponse (packet, "No thread specified in QRestoreRegisterState packet"); - else - return SendIllFormedResponse (packet, "No thread was is set with the Hg packet"); - } - - // Grab the register context for the thread. - NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); - if (!reg_context_sp) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); - return SendErrorResponse (0x15); - } - - // Retrieve register state buffer, then remove from the list. - DataBufferSP register_data_sp; - { - Mutex::Locker locker (m_saved_registers_mutex); - - // Find the register set buffer for the given save id. - auto it = m_saved_registers_map.find (save_id); - if (it == m_saved_registers_map.end ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " does not have a register set save buffer for id %" PRIu32, __FUNCTION__, m_debugged_process_sp->GetID (), save_id); - return SendErrorResponse (0x77); - } - register_data_sp = it->second; - - // Remove it from the map. - m_saved_registers_map.erase (it); - } - - Error error = reg_context_sp->WriteAllRegisterValues (register_data_sp); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " failed to restore all register values: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x77); - } - - return SendOKResponse(); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_vAttach (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("only supported for lldb-gdbserver"); - - // Consume the ';' after vAttach. - packet.SetFilePos (strlen ("vAttach")); - if (!packet.GetBytesLeft () || packet.GetChar () != ';') - return SendIllFormedResponse (packet, "vAttach missing expected ';'"); - - // Grab the PID to which we will attach (assume hex encoding). - lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID, 16); - if (pid == LLDB_INVALID_PROCESS_ID) - return SendIllFormedResponse (packet, "vAttach failed to parse the process id"); - - // Attempt to attach. - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s attempting to attach to pid %" PRIu64, __FUNCTION__, pid); - - Error error = AttachToProcess (pid); - - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to attach to pid %" PRIu64 ": %s\n", __FUNCTION__, pid, error.AsCString()); - return SendErrorResponse (0x01); - } - - // Notify we attached by sending a stop packet. - return SendStopReasonForState (m_debugged_process_sp->GetState (), true); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_D (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("only supported for lldb-gdbserver"); - - // Scope for mutex locker. - Mutex::Locker locker (m_spawned_pids_mutex); - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); - return SendErrorResponse (0x15); - } - - if (m_spawned_pids.find(m_debugged_process_sp->GetID ()) == m_spawned_pids.end()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to find PID %" PRIu64 " in spawned pids list", - __FUNCTION__, m_debugged_process_sp->GetID ()); - return SendErrorResponse (0x1); - } - - lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; - - // Consume the ';' after D. - packet.SetFilePos (1); - if (packet.GetBytesLeft ()) - { - if (packet.GetChar () != ';') - return SendIllFormedResponse (packet, "D missing expected ';'"); - - // Grab the PID from which we will detach (assume hex encoding). - pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID, 16); - if (pid == LLDB_INVALID_PROCESS_ID) - return SendIllFormedResponse (packet, "D failed to parse the process id"); - } - - if (pid != LLDB_INVALID_PROCESS_ID && - m_debugged_process_sp->GetID () != pid) - { - return SendIllFormedResponse (packet, "Invalid pid"); - } - - if (m_stdio_communication.IsConnected ()) - { - m_stdio_communication.StopReadThread (); - } - - const Error error = m_debugged_process_sp->Detach (); - if (error.Fail ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed to detach from pid %" PRIu64 ": %s\n", - __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); - return SendErrorResponse (0x01); - } - - m_spawned_pids.erase (m_debugged_process_sp->GetID ()); - return SendOKResponse (); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_qThreadStopInfo (StringExtractorGDBRemote &packet) -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // We don't support if we're not llgs. - if (!IsGdbServer()) - return SendUnimplementedResponse ("only supported for lldb-gdbserver"); - - packet.SetFilePos (strlen("qThreadStopInfo")); - const lldb::tid_t tid = packet.GetHexMaxU32 (false, LLDB_INVALID_THREAD_ID); - if (tid == LLDB_INVALID_THREAD_ID) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s failed, could not parse thread id from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); - return SendErrorResponse (0x15); - } - return SendStopReplyPacketForThread (tid); -} - -GDBRemoteCommunicationServer::PacketResult -GDBRemoteCommunicationServer::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet) -{ - // Only the gdb server handles this. - if (!IsGdbServer ()) - return SendUnimplementedResponse (packet.GetStringRef ().c_str ()); - - // Fail if we don't have a current process. - if (!m_debugged_process_sp || - m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) - return SendErrorResponse (68); - - packet.SetFilePos(strlen("qWatchpointSupportInfo")); - if (packet.GetBytesLeft() == 0) - return SendOKResponse(); - if (packet.GetChar() != ':') - return SendErrorResponse(67); - - uint32_t num = m_debugged_process_sp->GetMaxWatchpoints(); - StreamGDBRemote response; - response.Printf ("num:%d;", num); - return SendPacketNoLock(response.GetData(), response.GetSize()); -} - -void -GDBRemoteCommunicationServer::FlushInferiorOutput () -{ - // If we're not monitoring an inferior's terminal, ignore this. - if (!m_stdio_communication.IsConnected()) - return; - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s() called", __FUNCTION__); - - // FIXME implement a timeout on the join. - m_stdio_communication.JoinReadThread(); -} - -void -GDBRemoteCommunicationServer::MaybeCloseInferiorTerminalConnection () -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); - - // Tell the stdio connection to shut down. - if (m_stdio_communication.IsConnected()) - { - auto connection = m_stdio_communication.GetConnection(); - if (connection) - { - Error error; - connection->Disconnect (&error); - - if (error.Success ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s disconnect process terminal stdio - SUCCESS", __FUNCTION__); - } - else - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s disconnect process terminal stdio - FAIL: %s", __FUNCTION__, error.AsCString ()); - } - } - } -} - - -lldb_private::NativeThreadProtocolSP -GDBRemoteCommunicationServer::GetThreadFromSuffix (StringExtractorGDBRemote &packet) -{ - NativeThreadProtocolSP thread_sp; - - // We have no thread if we don't have a process. - if (!m_debugged_process_sp || m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) - return thread_sp; - - // If the client hasn't asked for thread suffix support, there will not be a thread suffix. - // Use the current thread in that case. - if (!m_thread_suffix_supported) - { - const lldb::tid_t current_tid = GetCurrentThreadID (); - if (current_tid == LLDB_INVALID_THREAD_ID) - return thread_sp; - else if (current_tid == 0) - { - // Pick a thread. - return m_debugged_process_sp->GetThreadAtIndex (0); - } - else - return m_debugged_process_sp->GetThreadByID (current_tid); - } - - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - - // Parse out the ';'. - if (packet.GetBytesLeft () < 1 || packet.GetChar () != ';') - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s gdb-remote parse error: expected ';' prior to start of thread suffix: packet contents = '%s'", __FUNCTION__, packet.GetStringRef ().c_str ()); - return thread_sp; - } - - if (!packet.GetBytesLeft ()) - return thread_sp; - - // Parse out thread: portion. - if (strncmp (packet.Peek (), "thread:", strlen("thread:")) != 0) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s gdb-remote parse error: expected 'thread:' but not found, packet contents = '%s'", __FUNCTION__, packet.GetStringRef ().c_str ()); - return thread_sp; - } - packet.SetFilePos (packet.GetFilePos () + strlen("thread:")); - const lldb::tid_t tid = packet.GetHexMaxU64(false, 0); - if (tid != 0) - return m_debugged_process_sp->GetThreadByID (tid); - - return thread_sp; -} - -lldb::tid_t -GDBRemoteCommunicationServer::GetCurrentThreadID () const -{ - if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) - { - // Use whatever the debug process says is the current thread id - // since the protocol either didn't specify or specified we want - // any/all threads marked as the current thread. - if (!m_debugged_process_sp) - return LLDB_INVALID_THREAD_ID; - return m_debugged_process_sp->GetCurrentThreadID (); - } - // Use the specific current thread id set by the gdb remote protocol. - return m_current_tid; -} - -uint32_t -GDBRemoteCommunicationServer::GetNextSavedRegistersID () -{ - Mutex::Locker locker (m_saved_registers_mutex); - return m_next_saved_registers_id++; -} - -void -GDBRemoteCommunicationServer::ClearProcessSpecificData () -{ - Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|GDBR_LOG_PROCESS)); - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s()", __FUNCTION__); - - // Clear any auxv cached data. - // *BSD impls should be able to do this too. -#if defined(__linux__) - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s clearing auxv buffer (previously %s)", - __FUNCTION__, - m_active_auxv_buffer_sp ? "was set" : "was not set"); - m_active_auxv_buffer_sp.reset (); -#endif -} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index dcf07844527e..992c6dffaf54 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -12,236 +12,53 @@ // C Includes // C++ Includes -#include <vector> -#include <set> -#include <unordered_map> +#include <functional> +#include <map> + // Other libraries and framework includes // Project includes #include "lldb/lldb-private-forward.h" -#include "lldb/Core/Communication.h" -#include "lldb/Host/Mutex.h" -#include "lldb/Target/Process.h" #include "GDBRemoteCommunication.h" -#include "lldb/Host/common/NativeProcessProtocol.h" +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { class ProcessGDBRemote; -class StringExtractorGDBRemote; -class GDBRemoteCommunicationServer : - public GDBRemoteCommunication, - public lldb_private::NativeProcessProtocol::NativeDelegate +class GDBRemoteCommunicationServer : public GDBRemoteCommunication { public: - typedef std::map<uint16_t, lldb::pid_t> PortMap; - - enum - { - eBroadcastBitRunPacketSent = kLoUserBroadcastBit - }; - //------------------------------------------------------------------ - // Constructors and Destructors - //------------------------------------------------------------------ - GDBRemoteCommunicationServer(bool is_platform); + using PortMap = std::map<uint16_t, lldb::pid_t>; + using PacketHandler = std::function<PacketResult(StringExtractorGDBRemote &packet, + Error &error, + bool &interrupt, + bool &quit)>; - GDBRemoteCommunicationServer(bool is_platform, - const lldb::PlatformSP& platform_sp, - lldb::DebuggerSP& debugger_sp); + GDBRemoteCommunicationServer(const char *comm_name, + const char *listener_name); virtual ~GDBRemoteCommunicationServer(); + void RegisterPacketHandler(StringExtractorGDBRemote::ServerPacketType packet_type, + PacketHandler handler); + PacketResult GetPacketAndSendResponse (uint32_t timeout_usec, - lldb_private::Error &error, + Error &error, bool &interrupt, bool &quit); - virtual bool - GetThreadSuffixSupported () override - { - return true; - } - // After connecting, do a little handshake with the client to make sure // we are at least communicating bool - HandshakeWithClient (lldb_private::Error *error_ptr); - - // Set both ports to zero to let the platform automatically bind to - // a port chosen by the OS. - void - SetPortMap (PortMap &&port_map) - { - m_port_map = port_map; - } - - //---------------------------------------------------------------------- - // If we are using a port map where we can only use certain ports, - // get the next available port. - // - // If we are using a port map and we are out of ports, return UINT16_MAX - // - // If we aren't using a port map, return 0 to indicate we should bind to - // port 0 and then figure out which port we used. - //---------------------------------------------------------------------- - uint16_t - GetNextAvailablePort () - { - if (m_port_map.empty()) - return 0; // Bind to port zero and get a port, we didn't have any limitations - - for (auto &pair : m_port_map) - { - if (pair.second == LLDB_INVALID_PROCESS_ID) - { - pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; - return pair.first; - } - } - return UINT16_MAX; - } - - bool - AssociatePortWithProcess (uint16_t port, lldb::pid_t pid) - { - PortMap::iterator pos = m_port_map.find(port); - if (pos != m_port_map.end()) - { - pos->second = pid; - return true; - } - return false; - } - - bool - FreePort (uint16_t port) - { - PortMap::iterator pos = m_port_map.find(port); - if (pos != m_port_map.end()) - { - pos->second = LLDB_INVALID_PROCESS_ID; - return true; - } - return false; - } - - bool - FreePortForProcess (lldb::pid_t pid) - { - if (!m_port_map.empty()) - { - for (auto &pair : m_port_map) - { - if (pair.second == pid) - { - pair.second = LLDB_INVALID_PROCESS_ID; - return true; - } - } - } - return false; - } - - void - SetPortOffset (uint16_t port_offset) - { - m_port_offset = port_offset; - } - - //------------------------------------------------------------------ - /// Specify the program to launch and its arguments. - /// - /// @param[in] args - /// The command line to launch. - /// - /// @param[in] argc - /// The number of elements in the args array of cstring pointers. - /// - /// @return - /// An Error object indicating the success or failure of making - /// the setting. - //------------------------------------------------------------------ - lldb_private::Error - SetLaunchArguments (const char *const args[], int argc); - - //------------------------------------------------------------------ - /// Specify the launch flags for the process. - /// - /// @param[in] launch_flags - /// The launch flags to use when launching this process. - /// - /// @return - /// An Error object indicating the success or failure of making - /// the setting. - //------------------------------------------------------------------ - lldb_private::Error - SetLaunchFlags (unsigned int launch_flags); - - //------------------------------------------------------------------ - /// Launch a process with the current launch settings. - /// - /// This method supports running an lldb-gdbserver or similar - /// server in a situation where the startup code has been provided - /// with all the information for a child process to be launched. - /// - /// @return - /// An Error object indicating the success or failure of the - /// launch. - //------------------------------------------------------------------ - lldb_private::Error - LaunchProcess (); - - //------------------------------------------------------------------ - /// Attach to a process. - /// - /// This method supports attaching llgs to a process accessible via the - /// configured Platform. - /// - /// @return - /// An Error object indicating the success or failure of the - /// attach operation. - //------------------------------------------------------------------ - lldb_private::Error - AttachToProcess (lldb::pid_t pid); - - //------------------------------------------------------------------ - // NativeProcessProtocol::NativeDelegate overrides - //------------------------------------------------------------------ - void - InitializeDelegate (lldb_private::NativeProcessProtocol *process) override; - - void - ProcessStateChanged (lldb_private::NativeProcessProtocol *process, lldb::StateType state) override; - - void - DidExec (lldb_private::NativeProcessProtocol *process) override; + HandshakeWithClient (Error *error_ptr); protected: - lldb::PlatformSP m_platform_sp; - lldb::thread_t m_async_thread; - lldb_private::ProcessLaunchInfo m_process_launch_info; - lldb_private::Error m_process_launch_error; - std::set<lldb::pid_t> m_spawned_pids; - lldb_private::Mutex m_spawned_pids_mutex; - lldb_private::ProcessInstanceInfoList m_proc_infos; - uint32_t m_proc_infos_index; - PortMap m_port_map; - uint16_t m_port_offset; - lldb::tid_t m_current_tid; - lldb::tid_t m_continue_tid; - lldb_private::Mutex m_debugged_process_mutex; - lldb_private::NativeProcessProtocolSP m_debugged_process_sp; - lldb::DebuggerSP m_debugger_sp; - Communication m_stdio_communication; + std::map<StringExtractorGDBRemote::ServerPacketType, PacketHandler> m_packet_handlers; bool m_exit_now; // use in asynchronous handling to indicate process should exit. - lldb::StateType m_inferior_prev_state; - bool m_thread_suffix_supported; - bool m_list_threads_in_stop_reply; - lldb::DataBufferSP m_active_auxv_buffer_sp; - lldb_private::Mutex m_saved_registers_mutex; - std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map; - uint32_t m_next_saved_registers_id; PacketResult SendUnimplementedResponse (const char *packet); @@ -255,306 +72,14 @@ protected: PacketResult SendOKResponse (); - PacketResult - SendONotification (const char *buffer, uint32_t len); - - PacketResult - SendWResponse (lldb_private::NativeProcessProtocol *process); - - PacketResult - SendStopReplyPacketForThread (lldb::tid_t tid); - - PacketResult - SendStopReasonForState (lldb::StateType process_state, bool flush_on_exit); - - PacketResult - Handle_A (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qLaunchSuccess (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qHostInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet); - - PacketResult - Handle_k (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qPlatform_chmod (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qProcessInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qProcessInfoPID (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qfProcessInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qsProcessInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qC (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qUserName (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qGroupName (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qSpeedTest (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QEnvironment (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QLaunchArch (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSetDisableASLR (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSetDetachOnError (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSetWorkingDir (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qGetWorkingDir (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QStartNoAckMode (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSetSTDIN (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSetSTDOUT (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSetSTDERR (StringExtractorGDBRemote &packet); - - PacketResult - Handle_C (StringExtractorGDBRemote &packet); - - PacketResult - Handle_c (StringExtractorGDBRemote &packet, bool skip_file_pos_adjustment = false); - - PacketResult - Handle_vCont (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vCont_actions (StringExtractorGDBRemote &packet); - - PacketResult - Handle_stop_reason (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_Open (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_Close (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_pRead (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_pWrite (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_Size (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_Mode (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_Exists (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_symlink (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_unlink (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_Stat (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vFile_MD5 (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qPlatform_shell (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qRegisterInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qfThreadInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qsThreadInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_p (StringExtractorGDBRemote &packet); - - PacketResult - Handle_P (StringExtractorGDBRemote &packet); - - PacketResult - Handle_H (StringExtractorGDBRemote &packet); - - PacketResult - Handle_I (StringExtractorGDBRemote &packet); - - PacketResult - Handle_interrupt (StringExtractorGDBRemote &packet); - - PacketResult - Handle_m (StringExtractorGDBRemote &packet); - - PacketResult - Handle_M (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qMemoryRegionInfoSupported (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qMemoryRegionInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_Z (StringExtractorGDBRemote &packet); - - PacketResult - Handle_z (StringExtractorGDBRemote &packet); - - PacketResult - Handle_s (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qSupported (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QThreadSuffixSupported (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QListThreadsInStopReply (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qXfer_auxv_read (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QSaveRegisterState (StringExtractorGDBRemote &packet); - - PacketResult - Handle_QRestoreRegisterState (StringExtractorGDBRemote &packet); - - PacketResult - Handle_vAttach (StringExtractorGDBRemote &packet); - - PacketResult - Handle_D (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qThreadStopInfo (StringExtractorGDBRemote &packet); - - PacketResult - Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet); - - void - SetCurrentThreadID (lldb::tid_t tid); - - lldb::tid_t - GetCurrentThreadID () const; - - void - SetContinueThreadID (lldb::tid_t tid); - - lldb::tid_t - GetContinueThreadID () const { return m_continue_tid; } - - lldb_private::Error - SetSTDIOFileDescriptor (int fd); - - static void - STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); - private: - bool - DebugserverProcessReaped (lldb::pid_t pid); - - static bool - ReapDebugserverProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, - int status); - - bool - DebuggedProcessReaped (lldb::pid_t pid); - - static bool - ReapDebuggedProcess (void *callback_baton, - lldb::pid_t pid, - bool exited, - int signal, - int status); - - bool - KillSpawnedProcess (lldb::pid_t pid); - - bool - IsGdbServer () - { - return !m_is_platform; - } - - /// Launch an inferior process from lldb-gdbserver - lldb_private::Error - LaunchProcessForDebugging (); - - /// Launch a process from lldb-platform - lldb_private::Error - LaunchPlatformProcess (); - - void - HandleInferiorState_Exited (lldb_private::NativeProcessProtocol *process); - - void - HandleInferiorState_Stopped (lldb_private::NativeProcessProtocol *process); - - void - FlushInferiorOutput (); - - lldb_private::NativeThreadProtocolSP - GetThreadFromSuffix (StringExtractorGDBRemote &packet); - - uint32_t - GetNextSavedRegistersID (); - - void - MaybeCloseInferiorTerminalConnection (); - - void - ClearProcessSpecificData (); - - bool - ShouldRedirectInferiorOutputOverGdbRemote (const lldb_private::ProcessLaunchInfo &launch_info) const; - //------------------------------------------------------------------ // For GDBRemoteCommunicationServer only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationServer); }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // liblldb_GDBRemoteCommunicationServer_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp new file mode 100644 index 000000000000..698854e5dfbd --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -0,0 +1,1341 @@ +//===-- GDBRemoteCommunicationServerCommon.cpp ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunicationServerCommon.h" + +#include <errno.h> + +// C Includes +// C++ Includes +#include <cstring> +#include <chrono> + +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamGDBRemote.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "ProcessGDBRemoteLog.h" +#include "Utility/StringExtractorGDBRemote.h" + +#ifdef __ANDROID__ +#include "lldb/Host/android/HostInfoAndroid.h" +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +#ifdef __ANDROID__ + const static uint32_t g_default_packet_timeout_sec = 20; // seconds +#else + const static uint32_t g_default_packet_timeout_sec = 0; // not specified +#endif + +//---------------------------------------------------------------------- +// GDBRemoteCommunicationServerCommon constructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon(const char *comm_name, const char *listener_name) : + GDBRemoteCommunicationServer (comm_name, listener_name), + m_spawned_pids (), + m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), + m_process_launch_info (), + m_process_launch_error (), + m_proc_infos (), + m_proc_infos_index (0), + m_thread_suffix_supported (false), + m_list_threads_in_stop_reply (false) +{ + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A, + &GDBRemoteCommunicationServerCommon::Handle_A); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QEnvironment, + &GDBRemoteCommunicationServerCommon::Handle_QEnvironment); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QEnvironmentHexEncoded, + &GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qfProcessInfo, + &GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qGroupName, + &GDBRemoteCommunicationServerCommon::Handle_qGroupName); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qHostInfo, + &GDBRemoteCommunicationServerCommon::Handle_qHostInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, + &GDBRemoteCommunicationServerCommon::Handle_qKillSpawnedProcess); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QLaunchArch, + &GDBRemoteCommunicationServerCommon::Handle_QLaunchArch); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess, + &GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QListThreadsInStopReply, + &GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qEcho, + &GDBRemoteCommunicationServerCommon::Handle_qEcho); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qModuleInfo, + &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod, + &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir, + &GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qPlatform_shell, + &GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID, + &GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError, + &GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetSTDERR, + &GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetSTDIN, + &GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT, + &GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qSpeedTest, + &GDBRemoteCommunicationServerCommon::Handle_qSpeedTest); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qsProcessInfo, + &GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode, + &GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qSupported, + &GDBRemoteCommunicationServerCommon::Handle_qSupported); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QThreadSuffixSupported, + &GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qUserName, + &GDBRemoteCommunicationServerCommon::Handle_qUserName); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_close, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Close); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_exists, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Exists); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_md5, + &GDBRemoteCommunicationServerCommon::Handle_vFile_MD5); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_mode, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Mode); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_open, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Open); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_pread, + &GDBRemoteCommunicationServerCommon::Handle_vFile_pRead); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_pwrite, + &GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_size, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Size); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_stat, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Stat); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_symlink, + &GDBRemoteCommunicationServerCommon::Handle_vFile_symlink); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vFile_unlink, + &GDBRemoteCommunicationServerCommon::Handle_vFile_unlink); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServerCommon::~GDBRemoteCommunicationServerCommon() +{ +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qHostInfo (StringExtractorGDBRemote &packet) +{ + StreamString response; + + // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00 + + ArchSpec host_arch(HostInfo::GetArchitecture()); + const llvm::Triple &host_triple = host_arch.GetTriple(); + response.PutCString("triple:"); + response.PutCStringAsRawHex8(host_triple.getTriple().c_str()); + response.Printf (";ptrsize:%u;",host_arch.GetAddressByteSize()); + + const char* distribution_id = host_arch.GetDistributionId ().AsCString (); + if (distribution_id) + { + response.PutCString("distribution_id:"); + response.PutCStringAsRawHex8(distribution_id); + response.PutCString(";"); + } + + // Only send out MachO info when lldb-platform/llgs is running on a MachO host. +#if defined(__APPLE__) + uint32_t cpu = host_arch.GetMachOCPUType(); + uint32_t sub = host_arch.GetMachOCPUSubType(); + if (cpu != LLDB_INVALID_CPUTYPE) + response.Printf ("cputype:%u;", cpu); + if (sub != LLDB_INVALID_CPUTYPE) + response.Printf ("cpusubtype:%u;", sub); + + if (cpu == ArchSpec::kCore_arm_any) + response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes. + else + response.Printf("watchpoint_exceptions_received:after;"); +#else + if (host_arch.GetMachine() == llvm::Triple::mips64 || + host_arch.GetMachine() == llvm::Triple::mips64el) + response.Printf("watchpoint_exceptions_received:before;"); + else + response.Printf("watchpoint_exceptions_received:after;"); +#endif + + switch (lldb::endian::InlHostByteOrder()) + { + case eByteOrderBig: response.PutCString ("endian:big;"); break; + case eByteOrderLittle: response.PutCString ("endian:little;"); break; + case eByteOrderPDP: response.PutCString ("endian:pdp;"); break; + default: response.PutCString ("endian:unknown;"); break; + } + + uint32_t major = UINT32_MAX; + uint32_t minor = UINT32_MAX; + uint32_t update = UINT32_MAX; + if (HostInfo::GetOSVersion(major, minor, update)) + { + if (major != UINT32_MAX) + { + response.Printf("os_version:%u", major); + if (minor != UINT32_MAX) + { + response.Printf(".%u", minor); + if (update != UINT32_MAX) + response.Printf(".%u", update); + } + response.PutChar(';'); + } + } + + std::string s; + if (HostInfo::GetOSBuildString(s)) + { + response.PutCString ("os_build:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } + if (HostInfo::GetOSKernelDescription(s)) + { + response.PutCString ("os_kernel:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } + +#if defined(__APPLE__) + +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + // For iOS devices, we are connected through a USB Mux so we never pretend + // to actually have a hostname as far as the remote lldb that is connecting + // to this lldb-platform is concerned + response.PutCString ("hostname:"); + response.PutCStringAsRawHex8("127.0.0.1"); + response.PutChar(';'); +#else // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + if (HostInfo::GetHostname(s)) + { + response.PutCString ("hostname:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } +#endif // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + +#else // #if defined(__APPLE__) + if (HostInfo::GetHostname(s)) + { + response.PutCString ("hostname:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } +#endif // #if defined(__APPLE__) + + if (g_default_packet_timeout_sec > 0) + response.Printf ("default_packet_timeout:%u;", g_default_packet_timeout_sec); + + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID (StringExtractorGDBRemote &packet) +{ + // Packet format: "qProcessInfoPID:%i" where %i is the pid + packet.SetFilePos (::strlen ("qProcessInfoPID:")); + lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID); + if (pid != LLDB_INVALID_PROCESS_ID) + { + ProcessInstanceInfo proc_info; + if (Host::GetProcessInfo (pid, proc_info)) + { + StreamString response; + CreateProcessInfoResponse (proc_info, response); + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + return SendErrorResponse (1); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo (StringExtractorGDBRemote &packet) +{ + m_proc_infos_index = 0; + m_proc_infos.Clear(); + + ProcessInstanceInfoMatch match_info; + packet.SetFilePos(::strlen ("qfProcessInfo")); + if (packet.GetChar() == ':') + { + + std::string key; + std::string value; + while (packet.GetNameColonValue(key, value)) + { + bool success = true; + if (key.compare("name") == 0) + { + StringExtractor extractor; + extractor.GetStringRef().swap(value); + extractor.GetHexByteString (value); + match_info.GetProcessInfo().GetExecutableFile().SetFile(value.c_str(), false); + } + else if (key.compare("name_match") == 0) + { + if (value.compare("equals") == 0) + { + match_info.SetNameMatchType (eNameMatchEquals); + } + else if (value.compare("starts_with") == 0) + { + match_info.SetNameMatchType (eNameMatchStartsWith); + } + else if (value.compare("ends_with") == 0) + { + match_info.SetNameMatchType (eNameMatchEndsWith); + } + else if (value.compare("contains") == 0) + { + match_info.SetNameMatchType (eNameMatchContains); + } + else if (value.compare("regex") == 0) + { + match_info.SetNameMatchType (eNameMatchRegularExpression); + } + else + { + success = false; + } + } + else if (key.compare("pid") == 0) + { + match_info.GetProcessInfo().SetProcessID (StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success)); + } + else if (key.compare("parent_pid") == 0) + { + match_info.GetProcessInfo().SetParentProcessID (StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success)); + } + else if (key.compare("uid") == 0) + { + match_info.GetProcessInfo().SetUserID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("gid") == 0) + { + match_info.GetProcessInfo().SetGroupID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("euid") == 0) + { + match_info.GetProcessInfo().SetEffectiveUserID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("egid") == 0) + { + match_info.GetProcessInfo().SetEffectiveGroupID (StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("all_users") == 0) + { + match_info.SetMatchAllUsers(Args::StringToBoolean(value.c_str(), false, &success)); + } + else if (key.compare("triple") == 0) + { + match_info.GetProcessInfo().GetArchitecture().SetTriple (value.c_str(), NULL); + } + else + { + success = false; + } + + if (!success) + return SendErrorResponse (2); + } + } + + if (Host::FindProcesses (match_info, m_proc_infos)) + { + // We found something, return the first item by calling the get + // subsequent process info packet handler... + return Handle_qsProcessInfo (packet); + } + return SendErrorResponse (3); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo (StringExtractorGDBRemote &packet) +{ + if (m_proc_infos_index < m_proc_infos.GetSize()) + { + StreamString response; + CreateProcessInfoResponse (m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response); + ++m_proc_infos_index; + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + return SendErrorResponse (4); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qUserName (StringExtractorGDBRemote &packet) +{ +#if !defined(LLDB_DISABLE_POSIX) + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("GDBRemoteCommunicationServerCommon::%s begin", __FUNCTION__); + + // Packet format: "qUserName:%i" where %i is the uid + packet.SetFilePos(::strlen ("qUserName:")); + uint32_t uid = packet.GetU32 (UINT32_MAX); + if (uid != UINT32_MAX) + { + std::string name; + if (HostInfo::LookupUserName(uid, name)) + { + StreamString response; + response.PutCStringAsRawHex8 (name.c_str()); + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + if (log) + log->Printf("GDBRemoteCommunicationServerCommon::%s end", __FUNCTION__); +#endif + return SendErrorResponse (5); + +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qGroupName (StringExtractorGDBRemote &packet) +{ +#if !defined(LLDB_DISABLE_POSIX) + // Packet format: "qGroupName:%i" where %i is the gid + packet.SetFilePos(::strlen ("qGroupName:")); + uint32_t gid = packet.GetU32 (UINT32_MAX); + if (gid != UINT32_MAX) + { + std::string name; + if (HostInfo::LookupGroupName(gid, name)) + { + StreamString response; + response.PutCStringAsRawHex8 (name.c_str()); + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } +#endif + return SendErrorResponse (6); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qSpeedTest (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("qSpeedTest:")); + + std::string key; + std::string value; + bool success = packet.GetNameColonValue(key, value); + if (success && key.compare("response_size") == 0) + { + uint32_t response_size = StringConvert::ToUInt32(value.c_str(), 0, 0, &success); + if (success) + { + if (response_size == 0) + return SendOKResponse(); + StreamString response; + uint32_t bytes_left = response_size; + response.PutCString("data:"); + while (bytes_left > 0) + { + if (bytes_left >= 26) + { + response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + bytes_left -= 26; + } + else + { + response.Printf ("%*.*s;", bytes_left, bytes_left, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + bytes_left = 0; + } + } + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + return SendErrorResponse (7); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); + + lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + + // verify that we know anything about this pid. + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + { + // not a pid we know about + return SendErrorResponse (10); + } + } + + // go ahead and attempt to kill the spawned process + if (KillSpawnedProcess (pid)) + return SendOKResponse (); + else + return SendErrorResponse (11); +} + +bool +GDBRemoteCommunicationServerCommon::KillSpawnedProcess (lldb::pid_t pid) +{ + // make sure we know about this process + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return false; + } + + // first try a SIGTERM (standard kill) + Host::Kill (pid, SIGTERM); + + // check if that worked + for (size_t i=0; i<10; ++i) + { + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + { + // it is now killed + return true; + } + } + usleep (10000); + } + + // check one more time after the final usleep + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + + // the launched process still lives. Now try killing it again, + // this time with an unblockable signal. + Host::Kill (pid, SIGKILL); + + for (size_t i=0; i<10; ++i) + { + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + { + // it is now killed + return true; + } + } + usleep (10000); + } + + // check one more time after the final usleep + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + + // no luck - the process still lives + return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Open (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:open:")); + std::string path; + packet.GetHexByteStringTerminatedBy(path,','); + if (!path.empty()) + { + if (packet.GetChar() == ',') + { + uint32_t flags = File::ConvertOpenOptionsForPOSIXOpen( + packet.GetHexMaxU32(false, 0)); + if (packet.GetChar() == ',') + { + mode_t mode = packet.GetHexMaxU32(false, 0600); + Error error; + const FileSpec path_spec{path, true}; + int fd = ::open(path_spec.GetCString(), flags, mode); + const int save_errno = fd == -1 ? errno : 0; + StreamString response; + response.PutChar('F'); + response.Printf("%i", fd); + if (save_errno) + response.Printf(",%i", save_errno); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + } + } + return SendErrorResponse(18); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Close (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:close:")); + int fd = packet.GetS32(-1); + Error error; + int err = -1; + int save_errno = 0; + if (fd >= 0) + { + err = close(fd); + save_errno = err == -1 ? errno : 0; + } + else + { + save_errno = EINVAL; + } + StreamString response; + response.PutChar('F'); + response.Printf("%i", err); + if (save_errno) + response.Printf(",%i", save_errno); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_pRead (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + // Not implemented on Windows + return SendUnimplementedResponse("GDBRemoteCommunicationServerCommon::Handle_vFile_pRead() unimplemented"); +#else + StreamGDBRemote response; + packet.SetFilePos(::strlen("vFile:pread:")); + int fd = packet.GetS32(-1); + if (packet.GetChar() == ',') + { + uint64_t count = packet.GetU64(UINT64_MAX); + if (packet.GetChar() == ',') + { + uint64_t offset = packet.GetU64(UINT32_MAX); + if (count == UINT64_MAX) + { + response.Printf("F-1:%i", EINVAL); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + + std::string buffer(count, 0); + const ssize_t bytes_read = ::pread (fd, &buffer[0], buffer.size(), offset); + const int save_errno = bytes_read == -1 ? errno : 0; + response.PutChar('F'); + response.Printf("%zi", bytes_read); + if (save_errno) + response.Printf(",%i", save_errno); + else + { + response.PutChar(';'); + response.PutEscapedBytes(&buffer[0], bytes_read); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + } + return SendErrorResponse(21); + +#endif +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + return SendUnimplementedResponse("GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite() unimplemented"); +#else + packet.SetFilePos(::strlen("vFile:pwrite:")); + + StreamGDBRemote response; + response.PutChar('F'); + + int fd = packet.GetU32(UINT32_MAX); + if (packet.GetChar() == ',') + { + off_t offset = packet.GetU64(UINT32_MAX); + if (packet.GetChar() == ',') + { + std::string buffer; + if (packet.GetEscapedBinaryData(buffer)) + { + const ssize_t bytes_written = ::pwrite (fd, buffer.data(), buffer.size(), offset); + const int save_errno = bytes_written == -1 ? errno : 0; + response.Printf("%zi", bytes_written); + if (save_errno) + response.Printf(",%i", save_errno); + } + else + { + response.Printf ("-1,%i", EINVAL); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + } + return SendErrorResponse(27); +#endif +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Size (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:size:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) + { + lldb::user_id_t retcode = FileSystem::GetFileSize(FileSpec(path.c_str(), false)); + StreamString response; + response.PutChar('F'); + response.PutHex64(retcode); + if (retcode == UINT64_MAX) + { + response.PutChar(','); + response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode() + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(22); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Mode (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:mode:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) + { + Error error; + const uint32_t mode = File::GetPermissions(FileSpec{path, true}, error); + StreamString response; + response.Printf("F%u", mode); + if (mode == 0 || error.Fail()) + response.Printf(",%i", (int)error.GetError()); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(23); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Exists (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:exists:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) + { + bool retcode = FileSystem::GetFileExists(FileSpec(path.c_str(), false)); + StreamString response; + response.PutChar('F'); + response.PutChar(','); + if (retcode) + response.PutChar('1'); + else + response.PutChar('0'); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(24); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_symlink (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:symlink:")); + std::string dst, src; + packet.GetHexByteStringTerminatedBy(dst, ','); + packet.GetChar(); // Skip ',' char + packet.GetHexByteString(src); + Error error = FileSystem::Symlink(FileSpec{src, true}, FileSpec{dst, false}); + StreamString response; + response.Printf("F%u,%u", error.GetError(), error.GetError()); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_unlink (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:unlink:")); + std::string path; + packet.GetHexByteString(path); + Error error = FileSystem::Unlink(FileSpec{path, true}); + StreamString response; + response.Printf("F%u,%u", error.GetError(), error.GetError()); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_shell:")); + std::string path; + std::string working_dir; + packet.GetHexByteStringTerminatedBy(path,','); + if (!path.empty()) + { + if (packet.GetChar() == ',') + { + // FIXME: add timeout to qPlatform_shell packet + // uint32_t timeout = packet.GetHexMaxU32(false, 32); + uint32_t timeout = 10; + if (packet.GetChar() == ',') + packet.GetHexByteString(working_dir); + int status, signo; + std::string output; + Error err = Host::RunShellCommand(path.c_str(), + FileSpec{working_dir, true}, + &status, &signo, &output, timeout); + StreamGDBRemote response; + if (err.Fail()) + { + response.PutCString("F,"); + response.PutHex32(UINT32_MAX); + } + else + { + response.PutCString("F,"); + response.PutHex32(status); + response.PutChar(','); + response.PutHex32(signo); + response.PutChar(','); + response.PutEscapedBytes(output.c_str(), output.size()); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + } + return SendErrorResponse(24); +} + + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Stat (StringExtractorGDBRemote &packet) +{ + return SendUnimplementedResponse("GDBRemoteCommunicationServerCommon::Handle_vFile_Stat() unimplemented"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_MD5 (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:MD5:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) + { + uint64_t a,b; + StreamGDBRemote response; + if (!FileSystem::CalculateMD5(FileSpec(path.c_str(), false), a, b)) + { + response.PutCString("F,"); + response.PutCString("x"); + } + else + { + response.PutCString("F,"); + response.PutHex64(a); + response.PutHex64(b); + } + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(25); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_mkdir:")); + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() == ',') + { + std::string path; + packet.GetHexByteString(path); + Error error = FileSystem::MakeDirectory(FileSpec{path, false}, mode); + + StreamGDBRemote response; + response.Printf("F%u", error.GetError()); + + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(20); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_chmod:")); + + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() == ',') + { + std::string path; + packet.GetHexByteString(path); + Error error = FileSystem::SetFilePermissions(FileSpec{path, true}, mode); + + StreamGDBRemote response; + response.Printf("F%u", error.GetError()); + + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + return SendErrorResponse(19); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qSupported (StringExtractorGDBRemote &packet) +{ + StreamGDBRemote response; + + // Features common to lldb-platform and llgs. + uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet size--debugger can always use less + response.Printf ("PacketSize=%x", max_packet_size); + + response.PutCString (";QStartNoAckMode+"); + response.PutCString (";QThreadSuffixSupported+"); + response.PutCString (";QListThreadsInStopReply+"); + response.PutCString (";qEcho+"); +#if defined(__linux__) + response.PutCString (";qXfer:auxv:read+"); +#endif + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported (StringExtractorGDBRemote &packet) +{ + m_thread_suffix_supported = true; + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply (StringExtractorGDBRemote &packet) +{ + m_list_threads_in_stop_reply = true; + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetDetachOnError:")); + if (packet.GetU32(0)) + m_process_launch_info.GetFlags().Set (eLaunchFlagDetachOnError); + else + m_process_launch_info.GetFlags().Clear (eLaunchFlagDetachOnError); + return SendOKResponse (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode (StringExtractorGDBRemote &packet) +{ + // Send response first before changing m_send_acks to we ack this packet + PacketResult packet_result = SendOKResponse (); + m_send_acks = false; + return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetSTDIN:")); + FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = false; + const bool write = true; + if (file_action.Open(STDIN_FILENO, FileSpec{path, false}, read, write)) + { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse (); + } + return SendErrorResponse (15); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetSTDOUT:")); + FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = true; + const bool write = false; + if (file_action.Open(STDOUT_FILENO, FileSpec{path, false}, read, write)) + { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse (); + } + return SendErrorResponse (16); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetSTDERR:")); + FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = true; + const bool write = false; + if (file_action.Open(STDERR_FILENO, FileSpec{path, false}, read, write)) + { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse (); + } + return SendErrorResponse (17); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess (StringExtractorGDBRemote &packet) +{ + if (m_process_launch_error.Success()) + return SendOKResponse(); + StreamString response; + response.PutChar('E'); + response.PutCString(m_process_launch_error.AsCString("<unknown error>")); + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QEnvironment (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QEnvironment:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) + { + m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek()); + return SendOKResponse (); + } + return SendErrorResponse (12); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("QEnvironmentHexEncoded:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) + { + std::string str; + packet.GetHexByteString(str); + m_process_launch_info.GetEnvironmentEntries().AppendArgument(str.c_str()); + return SendOKResponse(); + } + return SendErrorResponse(12); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QLaunchArch (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QLaunchArch:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) + { + const char* arch_triple = packet.Peek(); + ArchSpec arch_spec(arch_triple,NULL); + m_process_launch_info.SetArchitecture(arch_spec); + return SendOKResponse(); + } + return SendErrorResponse(13); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_A (StringExtractorGDBRemote &packet) +{ + // The 'A' packet is the most over designed packet ever here with + // redundant argument indexes, redundant argument lengths and needed hex + // encoded argument string values. Really all that is needed is a comma + // separated hex encoded argument value list, but we will stay true to the + // documented version of the 'A' packet here... + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + int actual_arg_index = 0; + + packet.SetFilePos(1); // Skip the 'A' + bool success = true; + while (success && packet.GetBytesLeft() > 0) + { + // Decode the decimal argument string length. This length is the + // number of hex nibbles in the argument string value. + const uint32_t arg_len = packet.GetU32(UINT32_MAX); + if (arg_len == UINT32_MAX) + success = false; + else + { + // Make sure the argument hex string length is followed by a comma + if (packet.GetChar() != ',') + success = false; + else + { + // Decode the argument index. We ignore this really because + // who would really send down the arguments in a random order??? + const uint32_t arg_idx = packet.GetU32(UINT32_MAX); + if (arg_idx == UINT32_MAX) + success = false; + else + { + // Make sure the argument index is followed by a comma + if (packet.GetChar() != ',') + success = false; + else + { + // Decode the argument string value from hex bytes + // back into a UTF8 string and make sure the length + // matches the one supplied in the packet + std::string arg; + if (packet.GetHexByteStringFixedLength(arg, arg_len) != (arg_len / 2)) + success = false; + else + { + // If there are any bytes left + if (packet.GetBytesLeft()) + { + if (packet.GetChar() != ',') + success = false; + } + + if (success) + { + if (arg_idx == 0) + m_process_launch_info.GetExecutableFile().SetFile(arg.c_str(), false); + m_process_launch_info.GetArguments().AppendArgument(arg.c_str()); + if (log) + log->Printf ("LLGSPacketHandler::%s added arg %d: \"%s\"", __FUNCTION__, actual_arg_index, arg.c_str ()); + ++actual_arg_index; + } + } + } + } + } + } + } + + if (success) + { + m_process_launch_error = LaunchProcess (); + if (m_process_launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + return SendOKResponse (); + } + else + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("LLGSPacketHandler::%s failed to launch exe: %s", + __FUNCTION__, + m_process_launch_error.AsCString()); + + } + } + return SendErrorResponse (8); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qEcho (StringExtractorGDBRemote &packet) +{ + // Just echo back the exact same packet for qEcho... + return SendPacketNoLock(packet.GetStringRef().c_str(), packet.GetStringRef().size()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qModuleInfo (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("qModuleInfo:")); + + std::string module_path; + packet.GetHexByteStringTerminatedBy(module_path, ';'); + if (module_path.empty()) + return SendErrorResponse (1); + + if (packet.GetChar() != ';') + return SendErrorResponse (2); + + std::string triple; + packet.GetHexByteString(triple); + ArchSpec arch(triple.c_str()); + + const FileSpec req_module_path_spec(module_path.c_str(), true); + const FileSpec module_path_spec = FindModuleFile(req_module_path_spec.GetPath(), arch); + const ModuleSpec module_spec(module_path_spec, arch); + + ModuleSpecList module_specs; + if (!ObjectFile::GetModuleSpecifications(module_path_spec, 0, 0, module_specs)) + return SendErrorResponse (3); + + ModuleSpec matched_module_spec; + if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + return SendErrorResponse (4); + + const auto file_offset = matched_module_spec.GetObjectOffset(); + const auto file_size = matched_module_spec.GetObjectSize(); + const auto uuid_str = matched_module_spec.GetUUID().GetAsString(""); + + StreamGDBRemote response; + + if (uuid_str.empty()) + { + std::string md5_hash; + if (!FileSystem::CalculateMD5AsString(matched_module_spec.GetFileSpec(), file_offset, file_size, md5_hash)) + return SendErrorResponse (5); + response.PutCString ("md5:"); + response.PutCStringAsRawHex8(md5_hash.c_str()); + } + else{ + response.PutCString ("uuid:"); + response.PutCStringAsRawHex8(uuid_str.c_str()); + } + response.PutChar(';'); + + const auto &module_arch = matched_module_spec.GetArchitecture(); + response.PutCString("triple:"); + response.PutCStringAsRawHex8( module_arch.GetTriple().getTriple().c_str()); + response.PutChar(';'); + + response.PutCString("file_path:"); + response.PutCStringAsRawHex8(module_path_spec.GetCString()); + response.PutChar(';'); + response.PutCString("file_offset:"); + response.PutHex64(file_offset); + response.PutChar(';'); + response.PutCString("file_size:"); + response.PutHex64(file_size); + response.PutChar(';'); + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +void +GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, + StreamString &response) +{ + response.Printf ("pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;", + proc_info.GetProcessID(), + proc_info.GetParentProcessID(), + proc_info.GetUserID(), + proc_info.GetGroupID(), + proc_info.GetEffectiveUserID(), + proc_info.GetEffectiveGroupID()); + response.PutCString ("name:"); + response.PutCStringAsRawHex8(proc_info.GetExecutableFile().GetCString()); + response.PutChar(';'); + const ArchSpec &proc_arch = proc_info.GetArchitecture(); + if (proc_arch.IsValid()) + { + const llvm::Triple &proc_triple = proc_arch.GetTriple(); + response.PutCString("triple:"); + response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); + response.PutChar(';'); + } +} + +void +GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse_DebugServerStyle ( + const ProcessInstanceInfo &proc_info, StreamString &response) +{ + response.Printf ("pid:%" PRIx64 ";parent-pid:%" PRIx64 ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;", + proc_info.GetProcessID(), + proc_info.GetParentProcessID(), + proc_info.GetUserID(), + proc_info.GetGroupID(), + proc_info.GetEffectiveUserID(), + proc_info.GetEffectiveGroupID()); + + const ArchSpec &proc_arch = proc_info.GetArchitecture(); + if (proc_arch.IsValid()) + { + const llvm::Triple &proc_triple = proc_arch.GetTriple(); +#if defined(__APPLE__) + // We'll send cputype/cpusubtype. + const uint32_t cpu_type = proc_arch.GetMachOCPUType(); + if (cpu_type != 0) + response.Printf ("cputype:%" PRIx32 ";", cpu_type); + + const uint32_t cpu_subtype = proc_arch.GetMachOCPUSubType(); + if (cpu_subtype != 0) + response.Printf ("cpusubtype:%" PRIx32 ";", cpu_subtype); + + const std::string vendor = proc_triple.getVendorName (); + if (!vendor.empty ()) + response.Printf ("vendor:%s;", vendor.c_str ()); +#else + // We'll send the triple. + response.PutCString("triple:"); + response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); + response.PutChar(';'); +#endif + std::string ostype = proc_triple.getOSName (); + // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64. + if (proc_triple.getVendor () == llvm::Triple::Apple) + { + switch (proc_triple.getArch ()) + { + case llvm::Triple::arm: + case llvm::Triple::aarch64: + ostype = "ios"; + break; + default: + // No change. + break; + } + } + response.Printf ("ostype:%s;", ostype.c_str ()); + + + switch (proc_arch.GetByteOrder ()) + { + case lldb::eByteOrderLittle: response.PutCString ("endian:little;"); break; + case lldb::eByteOrderBig: response.PutCString ("endian:big;"); break; + case lldb::eByteOrderPDP: response.PutCString ("endian:pdp;"); break; + default: + // Nothing. + break; + } + + if (proc_triple.isArch64Bit ()) + response.PutCString ("ptrsize:8;"); + else if (proc_triple.isArch32Bit ()) + response.PutCString ("ptrsize:4;"); + else if (proc_triple.isArch16Bit ()) + response.PutCString ("ptrsize:2;"); + } +} + +FileSpec +GDBRemoteCommunicationServerCommon::FindModuleFile(const std::string& module_path, + const ArchSpec& arch) +{ +#ifdef __ANDROID__ + return HostInfoAndroid::ResolveLibraryPath(module_path, arch); +#else + return FileSpec(module_path.c_str(), true); +#endif +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h new file mode 100644 index 000000000000..62b129bb18b9 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h @@ -0,0 +1,216 @@ +//===-- GDBRemoteCommunicationServerCommon.h --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServerCommon_h_ +#define liblldb_GDBRemoteCommunicationServerCommon_h_ + +// C Includes +// C++ Includes +#include <set> + +// Other libraries and framework includes +#include "lldb/lldb-private-forward.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "GDBRemoteCommunicationServer.h" +#include "GDBRemoteCommunicationServerCommon.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServerCommon : + public GDBRemoteCommunicationServer +{ +public: + GDBRemoteCommunicationServerCommon(const char *comm_name, const char *listener_name); + + virtual + ~GDBRemoteCommunicationServerCommon(); + +protected: + std::set<lldb::pid_t> m_spawned_pids; + Mutex m_spawned_pids_mutex; + ProcessLaunchInfo m_process_launch_info; + Error m_process_launch_error; + ProcessInstanceInfoList m_proc_infos; + uint32_t m_proc_infos_index; + bool m_thread_suffix_supported; + bool m_list_threads_in_stop_reply; + + PacketResult + Handle_A (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qHostInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qProcessInfoPID (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qfProcessInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qsProcessInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qUserName (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qGroupName (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qSpeedTest (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_Open (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_Close (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_pRead (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_pWrite (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_Size (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_Mode (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_Exists (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_symlink (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_unlink (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_Stat (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vFile_MD5 (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qEcho (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qModuleInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qPlatform_shell (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qPlatform_mkdir (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qPlatform_chmod (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qSupported (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QThreadSuffixSupported (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QListThreadsInStopReply (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetDetachOnError (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QStartNoAckMode (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetSTDIN (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetSTDOUT (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetSTDERR (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qLaunchSuccess (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QEnvironment (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QEnvironmentHexEncoded (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QLaunchArch (StringExtractorGDBRemote &packet); + + bool + KillSpawnedProcess (lldb::pid_t pid); + + static void + CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, + StreamString &response); + + static void + CreateProcessInfoResponse_DebugServerStyle (const ProcessInstanceInfo &proc_info, + StreamString &response); + + template <typename T> + void + RegisterMemberFunctionHandler (StringExtractorGDBRemote::ServerPacketType packet_type, + PacketResult (T::*handler) (StringExtractorGDBRemote& packet)) + { + RegisterPacketHandler(packet_type, + [this, handler] (StringExtractorGDBRemote packet, + Error &error, + bool &interrupt, + bool &quit) + { + return (static_cast<T*>(this)->*handler) (packet); + }); + } + + bool + GetThreadSuffixSupported () override + { + return true; + } + + //------------------------------------------------------------------ + /// Launch a process with the current launch settings. + /// + /// This method supports running an lldb-gdbserver or similar + /// server in a situation where the startup code has been provided + /// with all the information for a child process to be launched. + /// + /// @return + /// An Error object indicating the success or failure of the + /// launch. + //------------------------------------------------------------------ + virtual Error + LaunchProcess () = 0; + + virtual FileSpec + FindModuleFile (const std::string& module_path, const ArchSpec& arch); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServerCommon_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp new file mode 100644 index 000000000000..e8955ddbd6e4 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -0,0 +1,2787 @@ +//===-- GDBRemoteCommunicationServerLLGS.cpp --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <errno.h> + +#include "lldb/Host/Config.h" + +#include "GDBRemoteCommunicationServerLLGS.h" +#include "lldb/Core/StreamGDBRemote.h" + +// C Includes +// C++ Includes +#include <cstring> +#include <chrono> +#include <thread> + +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Debug.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Platform.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +// Project includes +#include "Utility/StringExtractorGDBRemote.h" +#include "Utility/UriParser.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace llvm; + +//---------------------------------------------------------------------- +// GDBRemote Errors +//---------------------------------------------------------------------- + +namespace +{ + enum GDBRemoteServerError + { + // Set to the first unused error number in literal form below + eErrorFirst = 29, + eErrorNoProcess = eErrorFirst, + eErrorResume, + eErrorExitStatus + }; +} + +//---------------------------------------------------------------------- +// GDBRemoteCommunicationServerLLGS constructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS( + const lldb::PlatformSP& platform_sp) : + GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"), + m_platform_sp (platform_sp), + m_async_thread (LLDB_INVALID_HOST_THREAD), + m_current_tid (LLDB_INVALID_THREAD_ID), + m_continue_tid (LLDB_INVALID_THREAD_ID), + m_debugged_process_mutex (Mutex::eMutexTypeRecursive), + m_debugged_process_sp (), + m_stdio_communication ("process.stdio"), + m_inferior_prev_state (StateType::eStateInvalid), + m_active_auxv_buffer_sp (), + m_saved_registers_mutex (), + m_saved_registers_map (), + m_next_saved_registers_id (1) +{ + assert(platform_sp); + RegisterPacketHandlers(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServerLLGS::~GDBRemoteCommunicationServerLLGS() +{ + Mutex::Locker locker (m_debugged_process_mutex); + + if (m_debugged_process_sp) + { + m_debugged_process_sp->Terminate (); + m_debugged_process_sp.reset (); + } +} + +void +GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() +{ + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C, + &GDBRemoteCommunicationServerLLGS::Handle_C); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c, + &GDBRemoteCommunicationServerLLGS::Handle_c); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D, + &GDBRemoteCommunicationServerLLGS::Handle_D); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H, + &GDBRemoteCommunicationServerLLGS::Handle_H); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I, + &GDBRemoteCommunicationServerLLGS::Handle_I); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_interrupt, + &GDBRemoteCommunicationServerLLGS::Handle_interrupt); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_m, + &GDBRemoteCommunicationServerLLGS::Handle_m); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, + &GDBRemoteCommunicationServerLLGS::Handle_M); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p, + &GDBRemoteCommunicationServerLLGS::Handle_p); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P, + &GDBRemoteCommunicationServerLLGS::Handle_P); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, + &GDBRemoteCommunicationServerLLGS::Handle_qC); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qfThreadInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress, + &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, + &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported, + &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qRegisterInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState, + &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState, + &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR, + &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, + &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qsThreadInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read, + &GDBRemoteCommunicationServerLLGS::Handle_qXfer_auxv_read); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s, + &GDBRemoteCommunicationServerLLGS::Handle_s); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_stop_reason, + &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ? + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vAttach, + &GDBRemoteCommunicationServerLLGS::Handle_vAttach); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vCont, + &GDBRemoteCommunicationServerLLGS::Handle_vCont); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_vCont_actions, + &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z, + &GDBRemoteCommunicationServerLLGS::Handle_Z); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, + &GDBRemoteCommunicationServerLLGS::Handle_z); + + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, + [this](StringExtractorGDBRemote packet, + Error &error, + bool &interrupt, + bool &quit) + { + quit = true; + return this->Handle_k (packet); + }); +} + +Error +GDBRemoteCommunicationServerLLGS::SetLaunchArguments (const char *const args[], int argc) +{ + if ((argc < 1) || !args || !args[0] || !args[0][0]) + return Error ("%s: no process command line specified to launch", __FUNCTION__); + + m_process_launch_info.SetArguments (const_cast<const char**> (args), true); + return Error (); +} + +Error +GDBRemoteCommunicationServerLLGS::SetLaunchFlags (unsigned int launch_flags) +{ + m_process_launch_info.GetFlags ().Set (launch_flags); + return Error (); +} + +Error +GDBRemoteCommunicationServerLLGS::LaunchProcess () +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (!m_process_launch_info.GetArguments ().GetArgumentCount ()) + return Error ("%s: no process command line specified to launch", __FUNCTION__); + + Error error; + { + Mutex::Locker locker (m_debugged_process_mutex); + assert (!m_debugged_process_sp && "lldb-gdbserver creating debugged process but one already exists"); + error = m_platform_sp->LaunchNativeProcess ( + m_process_launch_info, + *this, + m_debugged_process_sp); + } + + if (!error.Success ()) + { + fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0)); + return error; + } + + // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol + // as needed. + // llgs local-process debugging may specify PTY paths, which will make these + // file actions non-null + // process launch -i/e/o will also make these file actions non-null + // nullptr means that the traffic is expected to flow over gdb-remote protocol + if ( + m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr || + m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr || + m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr + ) + { + // nullptr means it's not redirected to file or pty (in case of LLGS local) + // at least one of stdio will be transferred pty<->gdb-remote + // we need to give the pty master handle to this object to read and/or write + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " setting up stdout/stderr redirection via $O gdb-remote commands", __FUNCTION__, m_debugged_process_sp->GetID ()); + + // Setup stdout/stderr mapping from inferior to $O + auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor (); + if (terminal_fd >= 0) + { + if (log) + log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd); + error = SetSTDIOFileDescriptor (terminal_fd); + if (error.Fail ()) + return error; + } + else + { + if (log) + log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); + } + } + else + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " skipping stdout/stderr redirection via $O: inferior will communicate over client-provided file descriptors", __FUNCTION__, m_debugged_process_sp->GetID ()); + } + + printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID ()); + + // Add to list of spawned processes. + lldb::pid_t pid; + if ((pid = m_process_launch_info.GetProcessID ()) != LLDB_INVALID_PROCESS_ID) + { + // add to spawned pids + Mutex::Locker locker (m_spawned_pids_mutex); + // On an lldb-gdbserver, we would expect there to be only one. + assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed"); + m_spawned_pids.insert (pid); + } + + return error; +} + +Error +GDBRemoteCommunicationServerLLGS::AttachToProcess (lldb::pid_t pid) +{ + Error error; + + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64, __FUNCTION__, pid); + + // Scope for mutex locker. + { + // Before we try to attach, make sure we aren't already monitoring something else. + Mutex::Locker locker (m_spawned_pids_mutex); + if (!m_spawned_pids.empty ()) + { + error.SetErrorStringWithFormat ("cannot attach to a process %" PRIu64 " when another process with pid %" PRIu64 " is being debugged.", pid, *m_spawned_pids.begin()); + return error; + } + + // Try to attach. + error = m_platform_sp->AttachNativeProcess (pid, *this, m_debugged_process_sp); + if (!error.Success ()) + { + fprintf (stderr, "%s: failed to attach to process %" PRIu64 ": %s", __FUNCTION__, pid, error.AsCString ()); + return error; + } + + // Setup stdout/stderr mapping from inferior. + auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor (); + if (terminal_fd >= 0) + { + if (log) + log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd); + error = SetSTDIOFileDescriptor (terminal_fd); + if (error.Fail ()) + return error; + } + else + { + if (log) + log->Printf ("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); + } + + printf ("Attached to process %" PRIu64 "...\n", pid); + + // Add to list of spawned processes. + assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed"); + m_spawned_pids.insert (pid); + + return error; + } +} + +void +GDBRemoteCommunicationServerLLGS::InitializeDelegate (NativeProcessProtocol *process) +{ + assert (process && "process cannot be NULL"); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s called with NativeProcessProtocol pid %" PRIu64 ", current state: %s", + __FUNCTION__, + process->GetID (), + StateAsCString (process->GetState ())); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendWResponse (NativeProcessProtocol *process) +{ + assert (process && "process cannot be NULL"); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // send W notification + ExitType exit_type = ExitType::eExitTypeInvalid; + int return_code = 0; + std::string exit_description; + + const bool got_exit_info = process->GetExitStatus (&exit_type, &return_code, exit_description); + if (!got_exit_info) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 ", failed to retrieve process exit status", __FUNCTION__, process->GetID ()); + + StreamGDBRemote response; + response.PutChar ('E'); + response.PutHex8 (GDBRemoteServerError::eErrorExitStatus); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + else + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 ", returning exit type %d, return code %d [%s]", __FUNCTION__, process->GetID (), exit_type, return_code, exit_description.c_str ()); + + StreamGDBRemote response; + + char return_type_code; + switch (exit_type) + { + case ExitType::eExitTypeExit: + return_type_code = 'W'; + break; + case ExitType::eExitTypeSignal: + return_type_code = 'X'; + break; + case ExitType::eExitTypeStop: + return_type_code = 'S'; + break; + case ExitType::eExitTypeInvalid: + return_type_code = 'E'; + break; + } + response.PutChar (return_type_code); + + // POSIX exit status limited to unsigned 8 bits. + response.PutHex8 (return_code); + + return SendPacketNoLock(response.GetData(), response.GetSize()); + } +} + +static void +AppendHexValue (StreamString &response, const uint8_t* buf, uint32_t buf_size, bool swap) +{ + int64_t i; + if (swap) + { + for (i = buf_size-1; i >= 0; i--) + response.PutHex8 (buf[i]); + } + else + { + for (i = 0; i < buf_size; i++) + response.PutHex8 (buf[i]); + } +} + +static void +WriteRegisterValueInHexFixedWidth (StreamString &response, + NativeRegisterContextSP ®_ctx_sp, + const RegisterInfo ®_info, + const RegisterValue *reg_value_p) +{ + RegisterValue reg_value; + if (!reg_value_p) + { + Error error = reg_ctx_sp->ReadRegister (®_info, reg_value); + if (error.Success ()) + reg_value_p = ®_value; + // else log. + } + + if (reg_value_p) + { + AppendHexValue (response, (const uint8_t*) reg_value_p->GetBytes (), reg_value_p->GetByteSize (), false); + } + else + { + // Zero-out any unreadable values. + if (reg_info.byte_size > 0) + { + std::basic_string<uint8_t> zeros(reg_info.byte_size, '\0'); + AppendHexValue (response, zeros.data(), zeros.size(), false); + } + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + + // Ensure we have a debugged process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse (50); + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s preparing packet for pid %" PRIu64 " tid %" PRIu64, + __FUNCTION__, m_debugged_process_sp->GetID (), tid); + + // Ensure we can get info on the given thread. + NativeThreadProtocolSP thread_sp (m_debugged_process_sp->GetThreadByID (tid)); + if (!thread_sp) + return SendErrorResponse (51); + + // Grab the reason this thread stopped. + struct ThreadStopInfo tid_stop_info; + std::string description; + if (!thread_sp->GetStopReason (tid_stop_info, description)) + return SendErrorResponse (52); + + // FIXME implement register handling for exec'd inferiors. + // if (tid_stop_info.reason == eStopReasonExec) + // { + // const bool force = true; + // InitializeRegisters(force); + // } + + StreamString response; + // Output the T packet with the thread + response.PutChar ('T'); + int signum = tid_stop_info.details.signal.signo; + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64, + __FUNCTION__, + m_debugged_process_sp->GetID (), + tid, + signum, + tid_stop_info.reason, + tid_stop_info.details.exception.type); + } + + // Print the signal number. + response.PutHex8 (signum & 0xff); + + // Include the tid. + response.Printf ("thread:%" PRIx64 ";", tid); + + // Include the thread name if there is one. + const std::string thread_name = thread_sp->GetName (); + if (!thread_name.empty ()) + { + size_t thread_name_len = thread_name.length (); + + if (::strcspn (thread_name.c_str (), "$#+-;:") == thread_name_len) + { + response.PutCString ("name:"); + response.PutCString (thread_name.c_str ()); + } + else + { + // The thread name contains special chars, send as hex bytes. + response.PutCString ("hexname:"); + response.PutCStringAsRawHex8 (thread_name.c_str ()); + } + response.PutChar (';'); + } + + // If a 'QListThreadsInStopReply' was sent to enable this feature, we + // will send all thread IDs back in the "threads" key whose value is + // a list of hex thread IDs separated by commas: + // "threads:10a,10b,10c;" + // This will save the debugger from having to send a pair of qfThreadInfo + // and qsThreadInfo packets, but it also might take a lot of room in the + // stop reply packet, so it must be enabled only on systems where there + // are no limits on packet lengths. + if (m_list_threads_in_stop_reply) + { + response.PutCString ("threads:"); + + uint32_t thread_index = 0; + NativeThreadProtocolSP listed_thread_sp; + for (listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index); listed_thread_sp; ++thread_index, listed_thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index)) + { + if (thread_index > 0) + response.PutChar (','); + response.Printf ("%" PRIx64, listed_thread_sp->GetID ()); + } + response.PutChar (';'); + } + + // + // Expedite registers. + // + + // Grab the register context. + NativeRegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext (); + if (reg_ctx_sp) + { + // Expedite all registers in the first register set (i.e. should be GPRs) that are not contained in other registers. + const RegisterSet *reg_set_p; + if (reg_ctx_sp->GetRegisterSetCount () > 0 && ((reg_set_p = reg_ctx_sp->GetRegisterSet (0)) != nullptr)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s expediting registers from set '%s' (registers set count: %zu)", __FUNCTION__, reg_set_p->name ? reg_set_p->name : "<unnamed-set>", reg_set_p->num_registers); + + for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) + { + const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex (*reg_num_p); + if (reg_info_p == nullptr) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to get register info for register set '%s', register index %" PRIu32, __FUNCTION__, reg_set_p->name ? reg_set_p->name : "<unnamed-set>", *reg_num_p); + } + else if (reg_info_p->value_regs == nullptr) + { + // Only expediate registers that are not contained in other registers. + RegisterValue reg_value; + Error error = reg_ctx_sp->ReadRegister (reg_info_p, reg_value); + if (error.Success ()) + { + response.Printf ("%.02x:", *reg_num_p); + WriteRegisterValueInHexFixedWidth(response, reg_ctx_sp, *reg_info_p, ®_value); + response.PutChar (';'); + } + else + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "<unnamed-register>", *reg_num_p, error.AsCString ()); + + } + } + } + } + } + + const char* reason_str = nullptr; + switch (tid_stop_info.reason) + { + case eStopReasonTrace: + reason_str = "trace"; + break; + case eStopReasonBreakpoint: + reason_str = "breakpoint"; + break; + case eStopReasonWatchpoint: + reason_str = "watchpoint"; + break; + case eStopReasonSignal: + reason_str = "signal"; + break; + case eStopReasonException: + reason_str = "exception"; + break; + case eStopReasonExec: + reason_str = "exec"; + break; + case eStopReasonInstrumentation: + case eStopReasonInvalid: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + case eStopReasonNone: + break; + } + if (reason_str != nullptr) + { + response.Printf ("reason:%s;", reason_str); + } + + if (!description.empty()) + { + // Description may contains special chars, send as hex bytes. + response.PutCString ("description:"); + response.PutCStringAsRawHex8 (description.c_str ()); + response.PutChar (';'); + } + else if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) + { + response.PutCString ("metype:"); + response.PutHex64 (tid_stop_info.details.exception.type); + response.PutCString (";mecount:"); + response.PutHex32 (tid_stop_info.details.exception.data_count); + response.PutChar (';'); + + for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) + { + response.PutCString ("medata:"); + response.PutHex64 (tid_stop_info.details.exception.data[i]); + response.PutChar (';'); + } + } + + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +void +GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited (NativeProcessProtocol *process) +{ + assert (process && "process cannot be NULL"); + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + // Send the exit result, and don't flush output. + // Note: flushing output here would join the inferior stdio reflection thread, which + // would gunk up the waitpid monitor thread that is calling this. + PacketResult result = SendStopReasonForState (StateType::eStateExited, false); + if (result != PacketResult::Success) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to send stop notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID ()); + } + + // Remove the process from the list of spawned pids. + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.erase (process->GetID ()) < 1) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to remove PID %" PRIu64 " from the spawned pids list", __FUNCTION__, process->GetID ()); + + } + } + + // FIXME can't do this yet - since process state propagation is currently + // synchronous, it is running off the NativeProcessProtocol's innards and + // will tear down the NPP while it still has code to execute. +#if 0 + // Clear the NativeProcessProtocol pointer. + { + Mutex::Locker locker (m_debugged_process_mutex); + m_debugged_process_sp.reset(); + } +#endif + + // Close the pipe to the inferior terminal i/o if we launched it + // and set one up. Otherwise, 'k' and its flush of stdio could + // end up waiting on a thread join that will never end. Consider + // adding a timeout to the connection thread join call so we + // can avoid that scenario altogether. + MaybeCloseInferiorTerminalConnection (); + + // We are ready to exit the debug monitor. + m_exit_now = true; +} + +void +GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped (NativeProcessProtocol *process) +{ + assert (process && "process cannot be NULL"); + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + // Send the stop reason unless this is the stop after the + // launch or attach. + switch (m_inferior_prev_state) + { + case eStateLaunching: + case eStateAttaching: + // Don't send anything per debugserver behavior. + break; + default: + // In all other cases, send the stop reason. + PacketResult result = SendStopReasonForState (StateType::eStateStopped, false); + if (result != PacketResult::Success) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to send stop notification for PID %" PRIu64 ", state: eStateExited", __FUNCTION__, process->GetID ()); + } + break; + } +} + +void +GDBRemoteCommunicationServerLLGS::ProcessStateChanged (NativeProcessProtocol *process, lldb::StateType state) +{ + assert (process && "process cannot be NULL"); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s called with NativeProcessProtocol pid %" PRIu64 ", state: %s", + __FUNCTION__, + process->GetID (), + StateAsCString (state)); + } + + // Make sure we get all of the pending stdout/stderr from the inferior + // and send it to the lldb host before we send the state change + // notification + m_stdio_communication.SynchronizeWithReadThread(); + + switch (state) + { + case StateType::eStateExited: + HandleInferiorState_Exited (process); + break; + + case StateType::eStateStopped: + HandleInferiorState_Stopped (process); + break; + + default: + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s didn't handle state change for pid %" PRIu64 ", new state: %s", + __FUNCTION__, + process->GetID (), + StateAsCString (state)); + } + break; + } + + // Remember the previous state reported to us. + m_inferior_prev_state = state; +} + +void +GDBRemoteCommunicationServerLLGS::DidExec (NativeProcessProtocol *process) +{ + ClearProcessSpecificData (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendONotification (const char *buffer, uint32_t len) +{ + if ((buffer == nullptr) || (len == 0)) + { + // Nothing to send. + return PacketResult::Success; + } + + StreamString response; + response.PutChar ('O'); + response.PutBytesAsRawHex8 (buffer, len); + + return SendPacketNoLock (response.GetData (), response.GetSize ()); +} + +Error +GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor (int fd) +{ + Error error; + + // Set up the Read Thread for reading/handling process I/O + std::unique_ptr<ConnectionFileDescriptor> conn_up (new ConnectionFileDescriptor (fd, true)); + if (!conn_up) + { + error.SetErrorString ("failed to create ConnectionFileDescriptor"); + return error; + } + + m_stdio_communication.SetCloseOnEOF (false); + m_stdio_communication.SetConnection (conn_up.release()); + if (!m_stdio_communication.IsConnected ()) + { + error.SetErrorString ("failed to set connection for inferior I/O communication"); + return error; + } + + // llgs local-process debugging may specify PTY paths, which will make these + // file actions non-null + // process launch -e/o will also make these file actions non-null + // nullptr means that the traffic is expected to flow over gdb-remote protocol + if ( + m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr || + m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr + ) + { + // output from the process must be forwarded over gdb-remote + // create a thread to read the handle and send the data + m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this); + m_stdio_communication.StartReadThread(); + } + + return error; +} + +void +GDBRemoteCommunicationServerLLGS::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len) +{ + GDBRemoteCommunicationServerLLGS *server = reinterpret_cast<GDBRemoteCommunicationServerLLGS*> (baton); + static_cast<void> (server->SendONotification (static_cast<const char *>(src), src_len)); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo (StringExtractorGDBRemote &packet) +{ + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse (68); + + lldb::pid_t pid = m_debugged_process_sp->GetID (); + + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse (1); + + ProcessInstanceInfo proc_info; + if (!Host::GetProcessInfo (pid, proc_info)) + return SendErrorResponse (1); + + StreamString response; + CreateProcessInfoResponse_DebugServerStyle(proc_info, response); + return SendPacketNoLock (response.GetData (), response.GetSize ()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qC (StringExtractorGDBRemote &packet) +{ + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse (68); + + // Make sure we set the current thread so g and p packets return + // the data the gdb will expect. + lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID (); + SetCurrentThreadID (tid); + + NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetCurrentThread (); + if (!thread_sp) + return SendErrorResponse (69); + + StreamString response; + response.Printf ("QC%" PRIx64, thread_sp->GetID ()); + + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +bool +GDBRemoteCommunicationServerLLGS::DebuggedProcessReaped (lldb::pid_t pid) +{ + // reap a process that we were debugging (but not debugserver) + Mutex::Locker locker (m_spawned_pids_mutex); + return m_spawned_pids.erase(pid) > 0; +} + +bool +GDBRemoteCommunicationServerLLGS::ReapDebuggedProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, // Zero for no signal + int status) // Exit value of process if signal is zero +{ + GDBRemoteCommunicationServerLLGS *server = (GDBRemoteCommunicationServerLLGS *)callback_baton; + server->DebuggedProcessReaped (pid); + return true; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_k (StringExtractorGDBRemote &packet) +{ + // shutdown all spawned processes + std::set<lldb::pid_t> spawned_pids_copy; + + // copy pids + { + Mutex::Locker locker (m_spawned_pids_mutex); + spawned_pids_copy.insert (m_spawned_pids.begin (), m_spawned_pids.end ()); + } + + // nuke the spawned processes + for (auto it = spawned_pids_copy.begin (); it != spawned_pids_copy.end (); ++it) + { + lldb::pid_t spawned_pid = *it; + if (!KillSpawnedProcess (spawned_pid)) + { + fprintf (stderr, "%s: failed to kill spawned pid %" PRIu64 ", ignoring.\n", __FUNCTION__, spawned_pid); + } + } + + FlushInferiorOutput (); + + // No OK response for kill packet. + // return SendOKResponse (); + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetDisableASLR:")); + if (packet.GetU32(0)) + m_process_launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + else + m_process_launch_info.GetFlags().Clear (eLaunchFlagDisableASLR); + return SendOKResponse (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos (::strlen ("QSetWorkingDir:")); + std::string path; + packet.GetHexByteString (path); + m_process_launch_info.SetWorkingDirectory(FileSpec{path, true}); + return SendOKResponse (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir (StringExtractorGDBRemote &packet) +{ + FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()}; + if (working_dir) + { + StreamString response; + response.PutCStringAsRawHex8(working_dir.GetCString()); + return SendPacketNoLock(response.GetData(), response.GetSize()); + } + + return SendErrorResponse(14); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_C (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + // Ensure we have a native process. + if (!m_debugged_process_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s no debugged process shared pointer", __FUNCTION__); + return SendErrorResponse (0x36); + } + + // Pull out the signal number. + packet.SetFilePos (::strlen ("C")); + if (packet.GetBytesLeft () < 1) + { + // Shouldn't be using a C without a signal. + return SendIllFormedResponse (packet, "C packet specified without signal."); + } + const uint32_t signo = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); + if (signo == std::numeric_limits<uint32_t>::max ()) + return SendIllFormedResponse (packet, "failed to parse signal number"); + + // Handle optional continue address. + if (packet.GetBytesLeft () > 0) + { + // FIXME add continue at address support for $C{signo}[;{continue-address}]. + if (*packet.Peek () == ';') + return SendUnimplementedResponse (packet.GetStringRef().c_str()); + else + return SendIllFormedResponse (packet, "unexpected content after $C{signal-number}"); + } + + ResumeActionList resume_actions (StateType::eStateRunning, 0); + Error error; + + // We have two branches: what to do if a continue thread is specified (in which case we target + // sending the signal to that thread), or when we don't have a continue thread set (in which + // case we send a signal to the process). + + // TODO discuss with Greg Clayton, make sure this makes sense. + + lldb::tid_t signal_tid = GetContinueThreadID (); + if (signal_tid != LLDB_INVALID_THREAD_ID) + { + // The resume action for the continue thread (or all threads if a continue thread is not set). + ResumeAction action = { GetContinueThreadID (), StateType::eStateRunning, static_cast<int> (signo) }; + + // Add the action for the continue thread (or all threads when the continue thread isn't present). + resume_actions.Append (action); + } + else + { + // Send the signal to the process since we weren't targeting a specific continue thread with the signal. + error = m_debugged_process_sp->Signal (signo); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to send signal for process %" PRIu64 ": %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + + return SendErrorResponse (0x52); + } + } + + // Resume the threads. + error = m_debugged_process_sp->Resume (resume_actions); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to resume threads for process %" PRIu64 ": %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + + return SendErrorResponse (0x38); + } + + // Don't send an "OK" packet; response is the stopped/exited message. + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_c (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + packet.SetFilePos (packet.GetFilePos() + ::strlen ("c")); + + // For now just support all continue. + const bool has_continue_address = (packet.GetBytesLeft () > 0); + if (has_continue_address) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s not implemented for c{address} variant [%s remains]", __FUNCTION__, packet.Peek ()); + return SendUnimplementedResponse (packet.GetStringRef().c_str()); + } + + // Ensure we have a native process. + if (!m_debugged_process_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s no debugged process shared pointer", __FUNCTION__); + return SendErrorResponse (0x36); + } + + // Build the ResumeActionList + ResumeActionList actions (StateType::eStateRunning, 0); + + Error error = m_debugged_process_sp->Resume (actions); + if (error.Fail ()) + { + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s c failed for process %" PRIu64 ": %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + } + return SendErrorResponse (GDBRemoteServerError::eErrorResume); + } + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s continued process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID ()); + + // No response required from continue. + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCont_actions (StringExtractorGDBRemote &packet) +{ + StreamString response; + response.Printf("vCont;c;C;s;S"); + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCont (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s handling vCont packet", __FUNCTION__); + + packet.SetFilePos (::strlen ("vCont")); + + if (packet.GetBytesLeft() == 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s missing action from vCont package", __FUNCTION__); + return SendIllFormedResponse (packet, "Missing action from vCont package"); + } + + // Check if this is all continue (no options or ";c"). + if (::strcmp (packet.Peek (), ";c") == 0) + { + // Move past the ';', then do a simple 'c'. + packet.SetFilePos (packet.GetFilePos () + 1); + return Handle_c (packet); + } + else if (::strcmp (packet.Peek (), ";s") == 0) + { + // Move past the ';', then do a simple 's'. + packet.SetFilePos (packet.GetFilePos () + 1); + return Handle_s (packet); + } + + // Ensure we have a native process. + if (!m_debugged_process_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s no debugged process shared pointer", __FUNCTION__); + return SendErrorResponse (0x36); + } + + ResumeActionList thread_actions; + + while (packet.GetBytesLeft () && *packet.Peek () == ';') + { + // Skip the semi-colon. + packet.GetChar (); + + // Build up the thread action. + ResumeAction thread_action; + thread_action.tid = LLDB_INVALID_THREAD_ID; + thread_action.state = eStateInvalid; + thread_action.signal = 0; + + const char action = packet.GetChar (); + switch (action) + { + case 'C': + thread_action.signal = packet.GetHexMaxU32 (false, 0); + if (thread_action.signal == 0) + return SendIllFormedResponse (packet, "Could not parse signal in vCont packet C action"); + // Fall through to next case... + + case 'c': + // Continue + thread_action.state = eStateRunning; + break; + + case 'S': + thread_action.signal = packet.GetHexMaxU32 (false, 0); + if (thread_action.signal == 0) + return SendIllFormedResponse (packet, "Could not parse signal in vCont packet S action"); + // Fall through to next case... + + case 's': + // Step + thread_action.state = eStateStepping; + break; + + default: + return SendIllFormedResponse (packet, "Unsupported vCont action"); + break; + } + + // Parse out optional :{thread-id} value. + if (packet.GetBytesLeft () && (*packet.Peek () == ':')) + { + // Consume the separator. + packet.GetChar (); + + thread_action.tid = packet.GetHexMaxU32 (false, LLDB_INVALID_THREAD_ID); + if (thread_action.tid == LLDB_INVALID_THREAD_ID) + return SendIllFormedResponse (packet, "Could not parse thread number in vCont packet"); + } + + thread_actions.Append (thread_action); + } + + Error error = m_debugged_process_sp->Resume (thread_actions); + if (error.Fail ()) + { + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s vCont failed for process %" PRIu64 ": %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + } + return SendErrorResponse (GDBRemoteServerError::eErrorResume); + } + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s continued process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID ()); + + // No response required from vCont. + return PacketResult::Success; +} + +void +GDBRemoteCommunicationServerLLGS::SetCurrentThreadID (lldb::tid_t tid) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s setting current thread id to %" PRIu64, __FUNCTION__, tid); + + m_current_tid = tid; + if (m_debugged_process_sp) + m_debugged_process_sp->SetCurrentThreadID (m_current_tid); +} + +void +GDBRemoteCommunicationServerLLGS::SetContinueThreadID (lldb::tid_t tid) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s setting continue thread id to %" PRIu64, __FUNCTION__, tid); + + m_continue_tid = tid; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_stop_reason (StringExtractorGDBRemote &packet) +{ + // Handle the $? gdbremote command. + + // If no process, indicate error + if (!m_debugged_process_sp) + return SendErrorResponse (02); + + return SendStopReasonForState (m_debugged_process_sp->GetState (), true); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReasonForState (lldb::StateType process_state, bool flush_on_exit) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + switch (process_state) + { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + // NOTE: gdb protocol doc looks like it should return $OK + // when everything is running (i.e. no stopped result). + return PacketResult::Success; // Ignore + + case eStateSuspended: + case eStateStopped: + case eStateCrashed: + { + lldb::tid_t tid = m_debugged_process_sp->GetCurrentThreadID (); + // Make sure we set the current thread so g and p packets return + // the data the gdb will expect. + SetCurrentThreadID (tid); + return SendStopReplyPacketForThread (tid); + } + + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + if (flush_on_exit) + FlushInferiorOutput (); + return SendWResponse(m_debugged_process_sp.get()); + + default: + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 ", current state reporting not handled: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + StateAsCString (process_state)); + } + break; + } + + return SendErrorResponse (0); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo (StringExtractorGDBRemote &packet) +{ + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse (68); + + // Ensure we have a thread. + NativeThreadProtocolSP thread_sp (m_debugged_process_sp->GetThreadAtIndex (0)); + if (!thread_sp) + return SendErrorResponse (69); + + // Get the register context for the first thread. + NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); + if (!reg_context_sp) + return SendErrorResponse (69); + + // Parse out the register number from the request. + packet.SetFilePos (strlen("qRegisterInfo")); + const uint32_t reg_index = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); + if (reg_index == std::numeric_limits<uint32_t>::max ()) + return SendErrorResponse (69); + + // Return the end of registers response if we've iterated one past the end of the register set. + if (reg_index >= reg_context_sp->GetUserRegisterCount ()) + return SendErrorResponse (69); + + const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return SendErrorResponse (69); + + // Build the reginfos response. + StreamGDBRemote response; + + response.PutCString ("name:"); + response.PutCString (reg_info->name); + response.PutChar (';'); + + if (reg_info->alt_name && reg_info->alt_name[0]) + { + response.PutCString ("alt-name:"); + response.PutCString (reg_info->alt_name); + response.PutChar (';'); + } + + response.Printf ("bitsize:%" PRIu32 ";offset:%" PRIu32 ";", reg_info->byte_size * 8, reg_info->byte_offset); + + switch (reg_info->encoding) + { + case eEncodingUint: response.PutCString ("encoding:uint;"); break; + case eEncodingSint: response.PutCString ("encoding:sint;"); break; + case eEncodingIEEE754: response.PutCString ("encoding:ieee754;"); break; + case eEncodingVector: response.PutCString ("encoding:vector;"); break; + default: break; + } + + switch (reg_info->format) + { + case eFormatBinary: response.PutCString ("format:binary;"); break; + case eFormatDecimal: response.PutCString ("format:decimal;"); break; + case eFormatHex: response.PutCString ("format:hex;"); break; + case eFormatFloat: response.PutCString ("format:float;"); break; + case eFormatVectorOfSInt8: response.PutCString ("format:vector-sint8;"); break; + case eFormatVectorOfUInt8: response.PutCString ("format:vector-uint8;"); break; + case eFormatVectorOfSInt16: response.PutCString ("format:vector-sint16;"); break; + case eFormatVectorOfUInt16: response.PutCString ("format:vector-uint16;"); break; + case eFormatVectorOfSInt32: response.PutCString ("format:vector-sint32;"); break; + case eFormatVectorOfUInt32: response.PutCString ("format:vector-uint32;"); break; + case eFormatVectorOfFloat32: response.PutCString ("format:vector-float32;"); break; + case eFormatVectorOfUInt128: response.PutCString ("format:vector-uint128;"); break; + default: break; + }; + + const char *const register_set_name = reg_context_sp->GetRegisterSetNameForRegisterAtIndex(reg_index); + if (register_set_name) + { + response.PutCString ("set:"); + response.PutCString (register_set_name); + response.PutChar (';'); + } + + if (reg_info->kinds[RegisterKind::eRegisterKindGCC] != LLDB_INVALID_REGNUM) + response.Printf ("gcc:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindGCC]); + + if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM) + response.Printf ("dwarf:%" PRIu32 ";", reg_info->kinds[RegisterKind::eRegisterKindDWARF]); + + switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) + { + case LLDB_REGNUM_GENERIC_PC: response.PutCString("generic:pc;"); break; + case LLDB_REGNUM_GENERIC_SP: response.PutCString("generic:sp;"); break; + case LLDB_REGNUM_GENERIC_FP: response.PutCString("generic:fp;"); break; + case LLDB_REGNUM_GENERIC_RA: response.PutCString("generic:ra;"); break; + case LLDB_REGNUM_GENERIC_FLAGS: response.PutCString("generic:flags;"); break; + case LLDB_REGNUM_GENERIC_ARG1: response.PutCString("generic:arg1;"); break; + case LLDB_REGNUM_GENERIC_ARG2: response.PutCString("generic:arg2;"); break; + case LLDB_REGNUM_GENERIC_ARG3: response.PutCString("generic:arg3;"); break; + case LLDB_REGNUM_GENERIC_ARG4: response.PutCString("generic:arg4;"); break; + case LLDB_REGNUM_GENERIC_ARG5: response.PutCString("generic:arg5;"); break; + case LLDB_REGNUM_GENERIC_ARG6: response.PutCString("generic:arg6;"); break; + case LLDB_REGNUM_GENERIC_ARG7: response.PutCString("generic:arg7;"); break; + case LLDB_REGNUM_GENERIC_ARG8: response.PutCString("generic:arg8;"); break; + default: break; + } + + if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) + { + response.PutCString ("container-regs:"); + int i = 0; + for (const uint32_t *reg_num = reg_info->value_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) + { + if (i > 0) + response.PutChar (','); + response.Printf ("%" PRIx32, *reg_num); + } + response.PutChar (';'); + } + + if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) + { + response.PutCString ("invalidate-regs:"); + int i = 0; + for (const uint32_t *reg_num = reg_info->invalidate_regs; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) + { + if (i > 0) + response.PutChar (','); + response.Printf ("%" PRIx32, *reg_num); + } + response.PutChar (';'); + } + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s() no process (%s), returning OK", __FUNCTION__, m_debugged_process_sp ? "invalid process id" : "null m_debugged_process_sp"); + return SendOKResponse (); + } + + StreamGDBRemote response; + response.PutChar ('m'); + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s() starting thread iteration", __FUNCTION__); + + NativeThreadProtocolSP thread_sp; + uint32_t thread_index; + for (thread_index = 0, thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index); + thread_sp; + ++thread_index, thread_sp = m_debugged_process_sp->GetThreadAtIndex (thread_index)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s() iterated thread %" PRIu32 "(%s, tid=0x%" PRIx64 ")", __FUNCTION__, thread_index, thread_sp ? "is not null" : "null", thread_sp ? thread_sp->GetID () : LLDB_INVALID_THREAD_ID); + if (thread_index > 0) + response.PutChar(','); + response.Printf ("%" PRIx64, thread_sp->GetID ()); + } + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s() finished thread iteration", __FUNCTION__); + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo (StringExtractorGDBRemote &packet) +{ + // FIXME for now we return the full thread list in the initial packet and always do nothing here. + return SendPacketNoLock ("l", 1); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_p (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Parse out the register number from the request. + packet.SetFilePos (strlen("p")); + const uint32_t reg_index = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); + if (reg_index == std::numeric_limits<uint32_t>::max ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, could not parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); + return SendErrorResponse (0x15); + } + + // Get the thread to use. + NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); + if (!thread_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no thread available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Get the thread's register context. + NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); + if (!reg_context_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); + return SendErrorResponse (0x15); + } + + // Return the end of registers response if we've iterated one past the end of the register set. + if (reg_index >= reg_context_sp->GetUserRegisterCount ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, requested register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount ()); + return SendErrorResponse (0x15); + } + + const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, requested register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); + return SendErrorResponse (0x15); + } + + // Build the reginfos response. + StreamGDBRemote response; + + // Retrieve the value + RegisterValue reg_value; + Error error = reg_context_sp->ReadRegister (reg_info, reg_value); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, read of requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString ()); + return SendErrorResponse (0x15); + } + + const uint8_t *const data = reinterpret_cast<const uint8_t*> (reg_value.GetBytes ()); + if (!data) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to get data bytes from requested register %" PRIu32, __FUNCTION__, reg_index); + return SendErrorResponse (0x15); + } + + // FIXME flip as needed to get data in big/little endian format for this host. + for (uint32_t i = 0; i < reg_value.GetByteSize (); ++i) + response.PutHex8 (data[i]); + + return SendPacketNoLock (response.GetData (), response.GetSize ()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_P (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Ensure there is more content. + if (packet.GetBytesLeft () < 1) + return SendIllFormedResponse (packet, "Empty P packet"); + + // Parse out the register number from the request. + packet.SetFilePos (strlen("P")); + const uint32_t reg_index = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); + if (reg_index == std::numeric_limits<uint32_t>::max ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, could not parse register number from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); + return SendErrorResponse (0x29); + } + + // Note debugserver would send an E30 here. + if ((packet.GetBytesLeft () < 1) || (packet.GetChar () != '=')) + return SendIllFormedResponse (packet, "P packet missing '=' char after register number"); + + // Get process architecture. + ArchSpec process_arch; + if (!m_debugged_process_sp || !m_debugged_process_sp->GetArchitecture (process_arch)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to retrieve inferior architecture", __FUNCTION__); + return SendErrorResponse (0x49); + } + + // Parse out the value. + uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register + size_t reg_size = packet.GetHexBytesAvail (reg_bytes, sizeof(reg_bytes)); + + // Get the thread to use. + NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); + if (!thread_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no thread available (thread index 0)", __FUNCTION__); + return SendErrorResponse (0x28); + } + + // Get the thread's register context. + NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); + if (!reg_context_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); + return SendErrorResponse (0x15); + } + + const RegisterInfo *reg_info = reg_context_sp->GetRegisterInfoAtIndex (reg_index); + if (!reg_info) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, requested register %" PRIu32 " returned NULL", __FUNCTION__, reg_index); + return SendErrorResponse (0x48); + } + + // Return the end of registers response if we've iterated one past the end of the register set. + if (reg_index >= reg_context_sp->GetUserRegisterCount ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, requested register %" PRIu32 " beyond register count %" PRIu32, __FUNCTION__, reg_index, reg_context_sp->GetUserRegisterCount ()); + return SendErrorResponse (0x47); + } + + if (reg_size != reg_info->byte_size) + { + return SendIllFormedResponse (packet, "P packet register size is incorrect"); + } + + // Build the reginfos response. + StreamGDBRemote response; + + RegisterValue reg_value (reg_bytes, reg_size, process_arch.GetByteOrder ()); + Error error = reg_context_sp->WriteRegister (reg_info, reg_value); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, write of requested register %" PRIu32 " (%s) failed: %s", __FUNCTION__, reg_index, reg_info->name, error.AsCString ()); + return SendErrorResponse (0x32); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_H (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Parse out which variant of $H is requested. + packet.SetFilePos (strlen("H")); + if (packet.GetBytesLeft () < 1) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, H command missing {g,c} variant", __FUNCTION__); + return SendIllFormedResponse (packet, "H command missing {g,c} variant"); + } + + const char h_variant = packet.GetChar (); + switch (h_variant) + { + case 'g': + break; + + case 'c': + break; + + default: + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c", __FUNCTION__, h_variant); + return SendIllFormedResponse (packet, "H variant unsupported, should be c or g"); + } + + // Parse out the thread number. + // FIXME return a parse success/fail value. All values are valid here. + const lldb::tid_t tid = packet.GetHexMaxU64 (false, std::numeric_limits<lldb::tid_t>::max ()); + + // Ensure we have the given thread when not specifying -1 (all threads) or 0 (any thread). + if (tid != LLDB_INVALID_THREAD_ID && tid != 0) + { + NativeThreadProtocolSP thread_sp (m_debugged_process_sp->GetThreadByID (tid)); + if (!thread_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64 " not found", __FUNCTION__, tid); + return SendErrorResponse (0x15); + } + } + + // Now switch the given thread type. + switch (h_variant) + { + case 'g': + SetCurrentThreadID (tid); + break; + + case 'c': + SetContinueThreadID (tid); + break; + + default: + assert (false && "unsupported $H variant - shouldn't get here"); + return SendIllFormedResponse (packet, "H variant unsupported, should be c or g"); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_I (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + packet.SetFilePos (::strlen("I")); + char tmp[4096]; + for (;;) + { + size_t read = packet.GetHexBytesAvail(tmp, sizeof(tmp)); + if (read == 0) + { + break; + } + // write directly to stdin *this might block if stdin buffer is full* + // TODO: enqueue this block in circular buffer and send window size to remote host + ConnectionStatus status; + Error error; + m_stdio_communication.Write(tmp, read, status, &error); + if (error.Fail()) + { + return SendErrorResponse (0x15); + } + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_interrupt (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Interrupt the process. + Error error = m_debugged_process_sp->Interrupt (); + if (error.Fail ()) + { + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed for process %" PRIu64 ": %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + } + return SendErrorResponse (GDBRemoteServerError::eErrorResume); + } + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s stopped process %" PRIu64, __FUNCTION__, m_debugged_process_sp->GetID ()); + + // No response required from stop all. + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_m (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Parse out the memory address. + packet.SetFilePos (strlen("m")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short m packet"); + + // Read the address. Punting on validation. + // FIXME replace with Hex U64 read with no default value that fails on failed read. + const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + + // Validate comma. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) + return SendIllFormedResponse(packet, "Comma sep missing in m packet"); + + // Get # bytes to read. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Length missing in m packet"); + + const uint64_t byte_count = packet.GetHexMaxU64(false, 0); + if (byte_count == 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s nothing to read: zero-length packet", __FUNCTION__); + return PacketResult::Success; + } + + // Allocate the response buffer. + std::string buf(byte_count, '\0'); + if (buf.empty()) + return SendErrorResponse (0x78); + + + // Retrieve the process memory. + size_t bytes_read = 0; + Error error = m_debugged_process_sp->ReadMemoryWithoutTrap(read_addr, &buf[0], byte_count, bytes_read); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to read. Error: %s", __FUNCTION__, m_debugged_process_sp->GetID (), read_addr, error.AsCString ()); + return SendErrorResponse (0x08); + } + + if (bytes_read == 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes", __FUNCTION__, m_debugged_process_sp->GetID (), read_addr, byte_count); + return SendErrorResponse (0x08); + } + + StreamGDBRemote response; + for (size_t i = 0; i < bytes_read; ++i) + response.PutHex8(buf[i]); + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_M (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Parse out the memory address. + packet.SetFilePos (strlen("M")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short M packet"); + + // Read the address. Punting on validation. + // FIXME replace with Hex U64 read with no default value that fails on failed read. + const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0); + + // Validate comma. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) + return SendIllFormedResponse(packet, "Comma sep missing in M packet"); + + // Get # bytes to read. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Length missing in M packet"); + + const uint64_t byte_count = packet.GetHexMaxU64(false, 0); + if (byte_count == 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s nothing to write: zero-length packet", __FUNCTION__); + return PacketResult::Success; + } + + // Validate colon. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':')) + return SendIllFormedResponse(packet, "Comma sep missing in M packet after byte length"); + + // Allocate the conversion buffer. + std::vector<uint8_t> buf(byte_count, 0); + if (buf.empty()) + return SendErrorResponse (0x78); + + // Convert the hex memory write contents to bytes. + StreamGDBRemote response; + const uint64_t convert_count = packet.GetHexBytes(&buf[0], byte_count, 0); + if (convert_count != byte_count) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": asked to write %" PRIu64 " bytes, but only found %" PRIu64 " to convert.", __FUNCTION__, m_debugged_process_sp->GetID (), write_addr, byte_count, convert_count); + return SendIllFormedResponse (packet, "M content byte length specified did not match hex-encoded content length"); + } + + // Write the process memory. + size_t bytes_written = 0; + Error error = m_debugged_process_sp->WriteMemory (write_addr, &buf[0], byte_count, bytes_written); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": failed to write. Error: %s", __FUNCTION__, m_debugged_process_sp->GetID (), write_addr, error.AsCString ()); + return SendErrorResponse (0x09); + } + + if (bytes_written == 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " mem 0x%" PRIx64 ": wrote 0 of %" PRIu64 " requested bytes", __FUNCTION__, m_debugged_process_sp->GetID (), write_addr, byte_count); + return SendErrorResponse (0x09); + } + + return SendOKResponse (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Currently only the NativeProcessProtocol knows if it can handle a qMemoryRegionInfoSupported + // request, but we're not guaranteed to be attached to a process. For now we'll assume the + // client only asks this when a process is being debugged. + + // Ensure we have a process running; otherwise, we can't figure this out + // since we won't have a NativeProcessProtocol. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Test if we can get any region back when asking for the region around NULL. + MemoryRegionInfo region_info; + const Error error = m_debugged_process_sp->GetMemoryRegionInfo (0, region_info); + if (error.Fail ()) + { + // We don't support memory region info collection for this NativeProcessProtocol. + return SendUnimplementedResponse (""); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Ensure we have a process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Parse out the memory address. + packet.SetFilePos (strlen("qMemoryRegionInfo:")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet"); + + // Read the address. Punting on validation. + const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + + StreamGDBRemote response; + + // Get the memory region info for the target address. + MemoryRegionInfo region_info; + const Error error = m_debugged_process_sp->GetMemoryRegionInfo (read_addr, region_info); + if (error.Fail ()) + { + // Return the error message. + + response.PutCString ("error:"); + response.PutCStringAsRawHex8 (error.AsCString ()); + response.PutChar (';'); + } + else + { + // Range start and size. + response.Printf ("start:%" PRIx64 ";size:%" PRIx64 ";", region_info.GetRange ().GetRangeBase (), region_info.GetRange ().GetByteSize ()); + + // Permissions. + if (region_info.GetReadable () || + region_info.GetWritable () || + region_info.GetExecutable ()) + { + // Write permissions info. + response.PutCString ("permissions:"); + + if (region_info.GetReadable ()) + response.PutChar ('r'); + if (region_info.GetWritable ()) + response.PutChar('w'); + if (region_info.GetExecutable()) + response.PutChar ('x'); + + response.PutChar (';'); + } + } + + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_Z (StringExtractorGDBRemote &packet) +{ + // Ensure we have a process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Parse out software or hardware breakpoint or watchpoint requested. + packet.SetFilePos (strlen("Z")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short Z packet, missing software/hardware specifier"); + + bool want_breakpoint = true; + bool want_hardware = false; + + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32 (eStoppointInvalid)); + switch (stoppoint_type) + { + case eBreakpointSoftware: + want_hardware = false; want_breakpoint = true; break; + case eBreakpointHardware: + want_hardware = true; want_breakpoint = true; break; + case eWatchpointWrite: + want_hardware = true; want_breakpoint = false; break; + case eWatchpointRead: + want_hardware = true; want_breakpoint = false; break; + case eWatchpointReadWrite: + want_hardware = true; want_breakpoint = false; break; + case eStoppointInvalid: + return SendIllFormedResponse(packet, "Z packet had invalid software/hardware specifier"); + + } + + if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') + return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after stoppoint type"); + + // Parse out the stoppoint address. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short Z packet, missing address"); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); + + if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') + return SendIllFormedResponse(packet, "Malformed Z packet, expecting comma after address"); + + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); + if (size == std::numeric_limits<uint32_t>::max ()) + return SendIllFormedResponse(packet, "Malformed Z packet, failed to parse size argument"); + + if (want_breakpoint) + { + // Try to set the breakpoint. + const Error error = m_debugged_process_sp->SetBreakpoint (addr, size, want_hardware); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " failed to set breakpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } + else + { + uint32_t watch_flags = + stoppoint_type == eWatchpointWrite + ? 0x1 // Write + : 0x3; // ReadWrite + + // Try to set the watchpoint. + const Error error = m_debugged_process_sp->SetWatchpoint ( + addr, size, watch_flags, want_hardware); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " failed to set watchpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_z (StringExtractorGDBRemote &packet) +{ + // Ensure we have a process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + // Parse out software or hardware breakpoint or watchpoint requested. + packet.SetFilePos (strlen("z")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short z packet, missing software/hardware specifier"); + + bool want_breakpoint = true; + + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32 (eStoppointInvalid)); + switch (stoppoint_type) + { + case eBreakpointHardware: want_breakpoint = true; break; + case eBreakpointSoftware: want_breakpoint = true; break; + case eWatchpointWrite: want_breakpoint = false; break; + case eWatchpointRead: want_breakpoint = false; break; + case eWatchpointReadWrite: want_breakpoint = false; break; + default: + return SendIllFormedResponse(packet, "z packet had invalid software/hardware specifier"); + + } + + if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') + return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after stoppoint type"); + + // Parse out the stoppoint address. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short z packet, missing address"); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); + + if ((packet.GetBytesLeft() < 1) || packet.GetChar () != ',') + return SendIllFormedResponse(packet, "Malformed z packet, expecting comma after address"); + + /* + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, std::numeric_limits<uint32_t>::max ()); + if (size == std::numeric_limits<uint32_t>::max ()) + return SendIllFormedResponse(packet, "Malformed z packet, failed to parse size argument"); + */ + + if (want_breakpoint) + { + // Try to clear the breakpoint. + const Error error = m_debugged_process_sp->RemoveBreakpoint (addr); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " failed to remove breakpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } + else + { + // Try to clear the watchpoint. + const Error error = m_debugged_process_sp->RemoveWatchpoint (addr); + if (error.Success ()) + return SendOKResponse (); + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " failed to remove watchpoint: %s", + __FUNCTION__, + m_debugged_process_sp->GetID (), + error.AsCString ()); + return SendErrorResponse (0x09); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_s (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_THREAD)); + + // Ensure we have a process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x32); + } + + // We first try to use a continue thread id. If any one or any all set, use the current thread. + // Bail out if we don't have a thread id. + lldb::tid_t tid = GetContinueThreadID (); + if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) + tid = GetCurrentThreadID (); + if (tid == LLDB_INVALID_THREAD_ID) + return SendErrorResponse (0x33); + + // Double check that we have such a thread. + // TODO investigate: on MacOSX we might need to do an UpdateThreads () here. + NativeThreadProtocolSP thread_sp = m_debugged_process_sp->GetThreadByID (tid); + if (!thread_sp || thread_sp->GetID () != tid) + return SendErrorResponse (0x33); + + // Create the step action for the given thread. + ResumeAction action = { tid, eStateStepping, 0 }; + + // Setup the actions list. + ResumeActionList actions; + actions.Append (action); + + // All other threads stop while we're single stepping a thread. + actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); + Error error = m_debugged_process_sp->Resume (actions); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " Resume() failed with error: %s", __FUNCTION__, m_debugged_process_sp->GetID (), tid, error.AsCString ()); + return SendErrorResponse(0x49); + } + + // No response here - the stop or exit will come from the resulting action. + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qXfer_auxv_read (StringExtractorGDBRemote &packet) +{ + // *BSD impls should be able to do this too. +#if defined(__linux__) + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Parse out the offset. + packet.SetFilePos (strlen("qXfer:auxv:read::")); + if (packet.GetBytesLeft () < 1) + return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing offset"); + + const uint64_t auxv_offset = packet.GetHexMaxU64 (false, std::numeric_limits<uint64_t>::max ()); + if (auxv_offset == std::numeric_limits<uint64_t>::max ()) + return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing offset"); + + // Parse out comma. + if (packet.GetBytesLeft () < 1 || packet.GetChar () != ',') + return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing comma after offset"); + + // Parse out the length. + const uint64_t auxv_length = packet.GetHexMaxU64 (false, std::numeric_limits<uint64_t>::max ()); + if (auxv_length == std::numeric_limits<uint64_t>::max ()) + return SendIllFormedResponse (packet, "qXfer:auxv:read:: packet missing length"); + + // Grab the auxv data if we need it. + if (!m_active_auxv_buffer_sp) + { + // Make sure we have a valid process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x10); + } + + // Grab the auxv data. + m_active_auxv_buffer_sp = Host::GetAuxvData (m_debugged_process_sp->GetID ()); + if (!m_active_auxv_buffer_sp || m_active_auxv_buffer_sp->GetByteSize () == 0) + { + // Hmm, no auxv data, call that an error. + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no auxv data retrieved", __FUNCTION__); + m_active_auxv_buffer_sp.reset (); + return SendErrorResponse (0x11); + } + } + + // FIXME find out if/how I lock the stream here. + + StreamGDBRemote response; + bool done_with_buffer = false; + + if (auxv_offset >= m_active_auxv_buffer_sp->GetByteSize ()) + { + // We have nothing left to send. Mark the buffer as complete. + response.PutChar ('l'); + done_with_buffer = true; + } + else + { + // Figure out how many bytes are available starting at the given offset. + const uint64_t bytes_remaining = m_active_auxv_buffer_sp->GetByteSize () - auxv_offset; + + // Figure out how many bytes we're going to read. + const uint64_t bytes_to_read = (auxv_length > bytes_remaining) ? bytes_remaining : auxv_length; + + // Mark the response type according to whether we're reading the remainder of the auxv data. + if (bytes_to_read >= bytes_remaining) + { + // There will be nothing left to read after this + response.PutChar ('l'); + done_with_buffer = true; + } + else + { + // There will still be bytes to read after this request. + response.PutChar ('m'); + } + + // Now write the data in encoded binary form. + response.PutEscapedBytes (m_active_auxv_buffer_sp->GetBytes () + auxv_offset, bytes_to_read); + } + + if (done_with_buffer) + m_active_auxv_buffer_sp.reset (); + + return SendPacketNoLock(response.GetData(), response.GetSize()); +#else + return SendUnimplementedResponse ("not implemented on this platform"); +#endif +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Move past packet name. + packet.SetFilePos (strlen ("QSaveRegisterState")); + + // Get the thread to use. + NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); + if (!thread_sp) + { + if (m_thread_suffix_supported) + return SendIllFormedResponse (packet, "No thread specified in QSaveRegisterState packet"); + else + return SendIllFormedResponse (packet, "No thread was is set with the Hg packet"); + } + + // Grab the register context for the thread. + NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); + if (!reg_context_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); + return SendErrorResponse (0x15); + } + + // Save registers to a buffer. + DataBufferSP register_data_sp; + Error error = reg_context_sp->ReadAllRegisterValues (register_data_sp); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to save all register values: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); + return SendErrorResponse (0x75); + } + + // Allocate a new save id. + const uint32_t save_id = GetNextSavedRegistersID (); + assert ((m_saved_registers_map.find (save_id) == m_saved_registers_map.end ()) && "GetNextRegisterSaveID() returned an existing register save id"); + + // Save the register data buffer under the save id. + { + Mutex::Locker locker (m_saved_registers_mutex); + m_saved_registers_map[save_id] = register_data_sp; + } + + // Write the response. + StreamGDBRemote response; + response.Printf ("%" PRIu32, save_id); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Parse out save id. + packet.SetFilePos (strlen ("QRestoreRegisterState:")); + if (packet.GetBytesLeft () < 1) + return SendIllFormedResponse (packet, "QRestoreRegisterState packet missing register save id"); + + const uint32_t save_id = packet.GetU32 (0); + if (save_id == 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s QRestoreRegisterState packet has malformed save id, expecting decimal uint32_t", __FUNCTION__); + return SendErrorResponse (0x76); + } + + // Get the thread to use. + NativeThreadProtocolSP thread_sp = GetThreadFromSuffix (packet); + if (!thread_sp) + { + if (m_thread_suffix_supported) + return SendIllFormedResponse (packet, "No thread specified in QRestoreRegisterState packet"); + else + return SendIllFormedResponse (packet, "No thread was is set with the Hg packet"); + } + + // Grab the register context for the thread. + NativeRegisterContextSP reg_context_sp (thread_sp->GetRegisterContext ()); + if (!reg_context_sp) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " failed, no register context available for the thread", __FUNCTION__, m_debugged_process_sp->GetID (), thread_sp->GetID ()); + return SendErrorResponse (0x15); + } + + // Retrieve register state buffer, then remove from the list. + DataBufferSP register_data_sp; + { + Mutex::Locker locker (m_saved_registers_mutex); + + // Find the register set buffer for the given save id. + auto it = m_saved_registers_map.find (save_id); + if (it == m_saved_registers_map.end ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " does not have a register set save buffer for id %" PRIu32, __FUNCTION__, m_debugged_process_sp->GetID (), save_id); + return SendErrorResponse (0x77); + } + register_data_sp = it->second; + + // Remove it from the map. + m_saved_registers_map.erase (it); + } + + Error error = reg_context_sp->WriteAllRegisterValues (register_data_sp); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " failed to restore all register values: %s", __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); + return SendErrorResponse (0x77); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttach (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Consume the ';' after vAttach. + packet.SetFilePos (strlen ("vAttach")); + if (!packet.GetBytesLeft () || packet.GetChar () != ';') + return SendIllFormedResponse (packet, "vAttach missing expected ';'"); + + // Grab the PID to which we will attach (assume hex encoding). + lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID, 16); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendIllFormedResponse (packet, "vAttach failed to parse the process id"); + + // Attempt to attach. + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s attempting to attach to pid %" PRIu64, __FUNCTION__, pid); + + Error error = AttachToProcess (pid); + + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to attach to pid %" PRIu64 ": %s\n", __FUNCTION__, pid, error.AsCString()); + return SendErrorResponse (0x01); + } + + // Notify we attached by sending a stop packet. + return SendStopReasonForState (m_debugged_process_sp->GetState (), true); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_D (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // Scope for mutex locker. + Mutex::Locker locker (m_spawned_pids_mutex); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + if (m_spawned_pids.find(m_debugged_process_sp->GetID ()) == m_spawned_pids.end()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to find PID %" PRIu64 " in spawned pids list", + __FUNCTION__, m_debugged_process_sp->GetID ()); + return SendErrorResponse (0x1); + } + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + // Consume the ';' after D. + packet.SetFilePos (1); + if (packet.GetBytesLeft ()) + { + if (packet.GetChar () != ';') + return SendIllFormedResponse (packet, "D missing expected ';'"); + + // Grab the PID from which we will detach (assume hex encoding). + pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID, 16); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendIllFormedResponse (packet, "D failed to parse the process id"); + } + + if (pid != LLDB_INVALID_PROCESS_ID && + m_debugged_process_sp->GetID () != pid) + { + return SendIllFormedResponse (packet, "Invalid pid"); + } + + if (m_stdio_communication.IsConnected ()) + { + m_stdio_communication.StopReadThread (); + } + + const Error error = m_debugged_process_sp->Detach (); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed to detach from pid %" PRIu64 ": %s\n", + __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); + return SendErrorResponse (0x01); + } + + m_spawned_pids.erase (m_debugged_process_sp->GetID ()); + return SendOKResponse (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + packet.SetFilePos (strlen("qThreadStopInfo")); + const lldb::tid_t tid = packet.GetHexMaxU32 (false, LLDB_INVALID_THREAD_ID); + if (tid == LLDB_INVALID_THREAD_ID) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s failed, could not parse thread id from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); + return SendErrorResponse (0x15); + } + return SendStopReplyPacketForThread (tid); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet) +{ + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse (68); + + packet.SetFilePos(strlen("qWatchpointSupportInfo")); + if (packet.GetBytesLeft() == 0) + return SendOKResponse(); + if (packet.GetChar() != ':') + return SendErrorResponse(67); + + uint32_t num = m_debugged_process_sp->GetMaxWatchpoints(); + StreamGDBRemote response; + response.Printf ("num:%d;", num); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress (StringExtractorGDBRemote &packet) +{ + // Fail if we don't have a current process. + if (!m_debugged_process_sp || + m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(67); + + packet.SetFilePos(strlen("qFileLoadAddress:")); + if (packet.GetBytesLeft() == 0) + return SendErrorResponse(68); + + std::string file_name; + packet.GetHexByteString(file_name); + + lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS; + Error error = m_debugged_process_sp->GetFileLoadAddress(file_name, file_load_address); + if (error.Fail()) + return SendErrorResponse(69); + + if (file_load_address == LLDB_INVALID_ADDRESS) + return SendErrorResponse(1); // File not loaded + + StreamGDBRemote response; + response.PutHex64(file_load_address); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +void +GDBRemoteCommunicationServerLLGS::FlushInferiorOutput () +{ + // If we're not monitoring an inferior's terminal, ignore this. + if (!m_stdio_communication.IsConnected()) + return; + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s() called", __FUNCTION__); + + // FIXME implement a timeout on the join. + m_stdio_communication.JoinReadThread(); +} + +void +GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection () +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Tell the stdio connection to shut down. + if (m_stdio_communication.IsConnected()) + { + auto connection = m_stdio_communication.GetConnection(); + if (connection) + { + Error error; + connection->Disconnect (&error); + + if (error.Success ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s disconnect process terminal stdio - SUCCESS", __FUNCTION__); + } + else + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s disconnect process terminal stdio - FAIL: %s", __FUNCTION__, error.AsCString ()); + } + } + } +} + + +NativeThreadProtocolSP +GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix (StringExtractorGDBRemote &packet) +{ + NativeThreadProtocolSP thread_sp; + + // We have no thread if we don't have a process. + if (!m_debugged_process_sp || m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID) + return thread_sp; + + // If the client hasn't asked for thread suffix support, there will not be a thread suffix. + // Use the current thread in that case. + if (!m_thread_suffix_supported) + { + const lldb::tid_t current_tid = GetCurrentThreadID (); + if (current_tid == LLDB_INVALID_THREAD_ID) + return thread_sp; + else if (current_tid == 0) + { + // Pick a thread. + return m_debugged_process_sp->GetThreadAtIndex (0); + } + else + return m_debugged_process_sp->GetThreadByID (current_tid); + } + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Parse out the ';'. + if (packet.GetBytesLeft () < 1 || packet.GetChar () != ';') + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse error: expected ';' prior to start of thread suffix: packet contents = '%s'", __FUNCTION__, packet.GetStringRef ().c_str ()); + return thread_sp; + } + + if (!packet.GetBytesLeft ()) + return thread_sp; + + // Parse out thread: portion. + if (strncmp (packet.Peek (), "thread:", strlen("thread:")) != 0) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse error: expected 'thread:' but not found, packet contents = '%s'", __FUNCTION__, packet.GetStringRef ().c_str ()); + return thread_sp; + } + packet.SetFilePos (packet.GetFilePos () + strlen("thread:")); + const lldb::tid_t tid = packet.GetHexMaxU64(false, 0); + if (tid != 0) + return m_debugged_process_sp->GetThreadByID (tid); + + return thread_sp; +} + +lldb::tid_t +GDBRemoteCommunicationServerLLGS::GetCurrentThreadID () const +{ + if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) + { + // Use whatever the debug process says is the current thread id + // since the protocol either didn't specify or specified we want + // any/all threads marked as the current thread. + if (!m_debugged_process_sp) + return LLDB_INVALID_THREAD_ID; + return m_debugged_process_sp->GetCurrentThreadID (); + } + // Use the specific current thread id set by the gdb remote protocol. + return m_current_tid; +} + +uint32_t +GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID () +{ + Mutex::Locker locker (m_saved_registers_mutex); + return m_next_saved_registers_id++; +} + +void +GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData () +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS|GDBR_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s()", __FUNCTION__); + + // Clear any auxv cached data. + // *BSD impls should be able to do this too. +#if defined(__linux__) + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s clearing auxv buffer (previously %s)", + __FUNCTION__, + m_active_auxv_buffer_sp ? "was set" : "was not set"); + m_active_auxv_buffer_sp.reset (); +#endif +} + +FileSpec +GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string& module_path, + const ArchSpec& arch) +{ + if (m_debugged_process_sp) + { + FileSpec file_spec; + if (m_debugged_process_sp->GetLoadedModuleFileSpec(module_path.c_str(), file_spec).Success()) + { + if (file_spec.Exists()) + return file_spec; + } + } + + return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch); +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h new file mode 100644 index 000000000000..1eda0b052bb7 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -0,0 +1,307 @@ +//===-- GDBRemoteCommunicationServerLLGS.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServerLLGS_h_ +#define liblldb_GDBRemoteCommunicationServerLLGS_h_ + +// C Includes +// C++ Includes +#include <unordered_map> + +// Other libraries and framework includes +#include "lldb/lldb-private-forward.h" +#include "lldb/Core/Communication.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/common/NativeProcessProtocol.h" + +// Project includes +#include "GDBRemoteCommunicationServerCommon.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServerLLGS : + public GDBRemoteCommunicationServerCommon, + public NativeProcessProtocol::NativeDelegate +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteCommunicationServerLLGS(const lldb::PlatformSP& platform_sp); + + virtual + ~GDBRemoteCommunicationServerLLGS(); + + //------------------------------------------------------------------ + /// Specify the program to launch and its arguments. + /// + /// @param[in] args + /// The command line to launch. + /// + /// @param[in] argc + /// The number of elements in the args array of cstring pointers. + /// + /// @return + /// An Error object indicating the success or failure of making + /// the setting. + //------------------------------------------------------------------ + Error + SetLaunchArguments (const char *const args[], int argc); + + //------------------------------------------------------------------ + /// Specify the launch flags for the process. + /// + /// @param[in] launch_flags + /// The launch flags to use when launching this process. + /// + /// @return + /// An Error object indicating the success or failure of making + /// the setting. + //------------------------------------------------------------------ + Error + SetLaunchFlags (unsigned int launch_flags); + + //------------------------------------------------------------------ + /// Launch a process with the current launch settings. + /// + /// This method supports running an lldb-gdbserver or similar + /// server in a situation where the startup code has been provided + /// with all the information for a child process to be launched. + /// + /// @return + /// An Error object indicating the success or failure of the + /// launch. + //------------------------------------------------------------------ + Error + LaunchProcess () override; + + //------------------------------------------------------------------ + /// Attach to a process. + /// + /// This method supports attaching llgs to a process accessible via the + /// configured Platform. + /// + /// @return + /// An Error object indicating the success or failure of the + /// attach operation. + //------------------------------------------------------------------ + Error + AttachToProcess (lldb::pid_t pid); + + //------------------------------------------------------------------ + // NativeProcessProtocol::NativeDelegate overrides + //------------------------------------------------------------------ + void + InitializeDelegate (NativeProcessProtocol *process) override; + + void + ProcessStateChanged (NativeProcessProtocol *process, lldb::StateType state) override; + + void + DidExec (NativeProcessProtocol *process) override; + +protected: + lldb::PlatformSP m_platform_sp; + lldb::thread_t m_async_thread; + lldb::tid_t m_current_tid; + lldb::tid_t m_continue_tid; + Mutex m_debugged_process_mutex; + NativeProcessProtocolSP m_debugged_process_sp; + Communication m_stdio_communication; + lldb::StateType m_inferior_prev_state; + lldb::DataBufferSP m_active_auxv_buffer_sp; + Mutex m_saved_registers_mutex; + std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map; + uint32_t m_next_saved_registers_id; + + PacketResult + SendONotification (const char *buffer, uint32_t len); + + PacketResult + SendWResponse (NativeProcessProtocol *process); + + PacketResult + SendStopReplyPacketForThread (lldb::tid_t tid); + + PacketResult + SendStopReasonForState (lldb::StateType process_state, bool flush_on_exit); + + PacketResult + Handle_k (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qProcessInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qC (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetDisableASLR (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetWorkingDir (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qGetWorkingDir (StringExtractorGDBRemote &packet); + + PacketResult + Handle_C (StringExtractorGDBRemote &packet); + + PacketResult + Handle_c (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vCont (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vCont_actions (StringExtractorGDBRemote &packet); + + PacketResult + Handle_stop_reason (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qRegisterInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qfThreadInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qsThreadInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_p (StringExtractorGDBRemote &packet); + + PacketResult + Handle_P (StringExtractorGDBRemote &packet); + + PacketResult + Handle_H (StringExtractorGDBRemote &packet); + + PacketResult + Handle_I (StringExtractorGDBRemote &packet); + + PacketResult + Handle_interrupt (StringExtractorGDBRemote &packet); + + PacketResult + Handle_m (StringExtractorGDBRemote &packet); + + PacketResult + Handle_M (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qMemoryRegionInfoSupported (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qMemoryRegionInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_Z (StringExtractorGDBRemote &packet); + + PacketResult + Handle_z (StringExtractorGDBRemote &packet); + + PacketResult + Handle_s (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qXfer_auxv_read (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSaveRegisterState (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QRestoreRegisterState (StringExtractorGDBRemote &packet); + + PacketResult + Handle_vAttach (StringExtractorGDBRemote &packet); + + PacketResult + Handle_D (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qThreadStopInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qFileLoadAddress (StringExtractorGDBRemote &packet); + + void + SetCurrentThreadID (lldb::tid_t tid); + + lldb::tid_t + GetCurrentThreadID () const; + + void + SetContinueThreadID (lldb::tid_t tid); + + lldb::tid_t + GetContinueThreadID () const { return m_continue_tid; } + + Error + SetSTDIOFileDescriptor (int fd); + + static void + STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + FileSpec + FindModuleFile (const std::string& module_path, const ArchSpec& arch) override; + +private: + bool + DebuggedProcessReaped (lldb::pid_t pid); + + static bool + ReapDebuggedProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, + int status); + + void + HandleInferiorState_Exited (NativeProcessProtocol *process); + + void + HandleInferiorState_Stopped (NativeProcessProtocol *process); + + void + FlushInferiorOutput (); + + NativeThreadProtocolSP + GetThreadFromSuffix (StringExtractorGDBRemote &packet); + + uint32_t + GetNextSavedRegistersID (); + + void + MaybeCloseInferiorTerminalConnection (); + + void + ClearProcessSpecificData (); + + void + RegisterPacketHandlers (); + + //------------------------------------------------------------------ + // For GDBRemoteCommunicationServerLLGS only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationServerLLGS); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServerLLGS_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp new file mode 100644 index 000000000000..f5e5d76f2e6f --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -0,0 +1,376 @@ +//===-- GDBRemoteCommunicationServerPlatform.cpp ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunicationServerPlatform.h" + +#include <errno.h> + +// C Includes +// C++ Includes +#include <cstring> +#include <chrono> + +// Other libraries and framework includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Target/FileAction.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "Utility/StringExtractorGDBRemote.h" +#include "Utility/UriParser.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +//---------------------------------------------------------------------- +// GDBRemoteCommunicationServerPlatform constructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform() : + GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"), + m_platform_sp (Platform::GetHostPlatform ()), + m_port_map (), + m_port_offset(0) +{ + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, + &GDBRemoteCommunicationServerPlatform::Handle_qC); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, + &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, + &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfo, + &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, + &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir); + + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt, + [this](StringExtractorGDBRemote packet, + Error &error, + bool &interrupt, + bool &quit) + { + error.SetErrorString("interrupt received"); + interrupt = true; + return PacketResult::Success; + }); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() +{ +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + return SendErrorResponse(9); +#else + // Spawn a local debugserver as a platform so we can then attach or launch + // a process... + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() called", __FUNCTION__); + + // Sleep and wait a bit for debugserver to start to listen... + ConnectionFileDescriptor file_conn; + std::string hostname; + // TODO: /tmp/ should not be hardcoded. User might want to override /tmp + // with the TMPDIR environment variable + packet.SetFilePos(::strlen ("qLaunchGDBServer;")); + std::string name; + std::string value; + uint16_t port = UINT16_MAX; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("host") == 0) + hostname.swap(value); + else if (name.compare ("port") == 0) + port = StringConvert::ToUInt32(value.c_str(), 0, 0); + } + if (port == UINT16_MAX) + port = GetNextAvailablePort(); + + // Spawn a new thread to accept the port that gets bound after + // binding to port 0 (zero). + + // ignore the hostname send from the remote end, just use the ip address + // that we're currently communicating with as the hostname + + // Spawn a debugserver and try to get the port it listens to. + ProcessLaunchInfo debugserver_launch_info; + if (hostname.empty()) + hostname = "127.0.0.1"; + if (log) + log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port); + + // Do not run in a new session so that it can not linger after the + // platform closes. + debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); + debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); + + std::string platform_scheme; + std::string platform_ip; + int platform_port; + std::string platform_path; + bool ok = UriParser::Parse(GetConnection()->GetURI().c_str(), platform_scheme, platform_ip, platform_port, platform_path); + assert(ok); + Error error = StartDebugserverProcess ( + platform_ip.c_str(), + port, + debugserver_launch_info, + port); + + lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); + + + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + Mutex::Locker locker (m_spawned_pids_mutex); + m_spawned_pids.insert(debugserver_pid); + if (port > 0) + AssociatePortWithProcess(port, debugserver_pid); + } + else + { + if (port > 0) + FreePort (port); + } + + if (error.Success()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid); + + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); + assert (response_len < (int)sizeof(response)); + PacketResult packet_result = SendPacketNoLock (response, response_len); + + if (packet_result != PacketResult::Success) + { + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + ::kill (debugserver_pid, SIGINT); + } + return packet_result; + } + else + { + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ()); + } + return SendErrorResponse (9); +#endif +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo (StringExtractorGDBRemote &packet) +{ + lldb::pid_t pid = m_process_launch_info.GetProcessID (); + m_process_launch_info.Clear (); + + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse (1); + + ProcessInstanceInfo proc_info; + if (!Host::GetProcessInfo (pid, proc_info)) + return SendErrorResponse (1); + + StreamString response; + CreateProcessInfoResponse_DebugServerStyle(proc_info, response); + return SendPacketNoLock (response.GetData (), response.GetSize ()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir (StringExtractorGDBRemote &packet) +{ + // If this packet is sent to a platform, then change the current working directory + + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) == NULL) + return SendErrorResponse(errno); + + StreamString response; + response.PutBytesAsRawHex8(cwd, strlen(cwd)); + return SendPacketNoLock(response.GetData(), response.GetSize()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos (::strlen ("QSetWorkingDir:")); + std::string path; + packet.GetHexByteString (path); + + // If this packet is sent to a platform, then change the current working directory + if (::chdir(path.c_str()) != 0) + return SendErrorResponse (errno); + return SendOKResponse (); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qC (StringExtractorGDBRemote &packet) +{ + // NOTE: lldb should now be using qProcessInfo for process IDs. This path here + // should not be used. It is reporting process id instead of thread id. The + // correct answer doesn't seem to make much sense for lldb-platform. + // CONSIDER: flip to "unsupported". + lldb::pid_t pid = m_process_launch_info.GetProcessID(); + + StreamString response; + response.Printf("QC%" PRIx64, pid); + + // If we launch a process and this GDB server is acting as a platform, + // then we need to clear the process launch state so we can start + // launching another process. In order to launch a process a bunch or + // packets need to be sent: environment packets, working directory, + // disable ASLR, and many more settings. When we launch a process we + // then need to know when to clear this information. Currently we are + // selecting the 'qC' packet as that packet which seems to make the most + // sense. + if (pid != LLDB_INVALID_PROCESS_ID) + { + m_process_launch_info.Clear(); + } + + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +bool +GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped (lldb::pid_t pid) +{ + Mutex::Locker locker (m_spawned_pids_mutex); + FreePortForProcess(pid); + return m_spawned_pids.erase(pid) > 0; +} + +bool +GDBRemoteCommunicationServerPlatform::ReapDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, // Zero for no signal + int status) // Exit value of process if signal is zero +{ + GDBRemoteCommunicationServerPlatform *server = (GDBRemoteCommunicationServerPlatform *)callback_baton; + server->DebugserverProcessReaped (pid); + return true; +} + +Error +GDBRemoteCommunicationServerPlatform::LaunchProcess () +{ + if (!m_process_launch_info.GetArguments ().GetArgumentCount ()) + return Error ("%s: no process command line specified to launch", __FUNCTION__); + + // specify the process monitor if not already set. This should + // generally be what happens since we need to reap started + // processes. + if (!m_process_launch_info.GetMonitorProcessCallback ()) + m_process_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); + + Error error = m_platform_sp->LaunchProcess (m_process_launch_info); + if (!error.Success ()) + { + fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0)); + return error; + } + + printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID()); + + // add to list of spawned processes. On an lldb-gdbserver, we + // would expect there to be only one. + const auto pid = m_process_launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) + { + // add to spawned pids + Mutex::Locker locker (m_spawned_pids_mutex); + m_spawned_pids.insert(pid); + } + + return error; +} + +void +GDBRemoteCommunicationServerPlatform::SetPortMap (PortMap &&port_map) +{ + m_port_map = port_map; +} + +uint16_t +GDBRemoteCommunicationServerPlatform::GetNextAvailablePort () +{ + if (m_port_map.empty()) + return 0; // Bind to port zero and get a port, we didn't have any limitations + + for (auto &pair : m_port_map) + { + if (pair.second == LLDB_INVALID_PROCESS_ID) + { + pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; + return pair.first; + } + } + return UINT16_MAX; +} + +bool +GDBRemoteCommunicationServerPlatform::AssociatePortWithProcess (uint16_t port, lldb::pid_t pid) +{ + PortMap::iterator pos = m_port_map.find(port); + if (pos != m_port_map.end()) + { + pos->second = pid; + return true; + } + return false; +} + +bool +GDBRemoteCommunicationServerPlatform::FreePort (uint16_t port) +{ + PortMap::iterator pos = m_port_map.find(port); + if (pos != m_port_map.end()) + { + pos->second = LLDB_INVALID_PROCESS_ID; + return true; + } + return false; +} + +bool +GDBRemoteCommunicationServerPlatform::FreePortForProcess (lldb::pid_t pid) +{ + if (!m_port_map.empty()) + { + for (auto &pair : m_port_map) + { + if (pair.second == pid) + { + pair.second = LLDB_INVALID_PROCESS_ID; + return true; + } + } + } + return false; +} + +void +GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset) +{ + m_port_offset = port_offset; +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h new file mode 100644 index 000000000000..4124b0424f5d --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -0,0 +1,102 @@ +//===-- GDBRemoteCommunicationServerPlatform.h ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServerPlatform_h_ +#define liblldb_GDBRemoteCommunicationServerPlatform_h_ + +#include "GDBRemoteCommunicationServerCommon.h" + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteCommunicationServerPlatform : + public GDBRemoteCommunicationServerCommon +{ +public: + typedef std::map<uint16_t, lldb::pid_t> PortMap; + + GDBRemoteCommunicationServerPlatform(); + + virtual + ~GDBRemoteCommunicationServerPlatform(); + + Error + LaunchProcess () override; + + // Set both ports to zero to let the platform automatically bind to + // a port chosen by the OS. + void + SetPortMap (PortMap &&port_map); + + //---------------------------------------------------------------------- + // If we are using a port map where we can only use certain ports, + // get the next available port. + // + // If we are using a port map and we are out of ports, return UINT16_MAX + // + // If we aren't using a port map, return 0 to indicate we should bind to + // port 0 and then figure out which port we used. + //---------------------------------------------------------------------- + uint16_t + GetNextAvailablePort (); + + bool + AssociatePortWithProcess (uint16_t port, lldb::pid_t pid); + + bool + FreePort (uint16_t port); + + bool + FreePortForProcess (lldb::pid_t pid); + + void + SetPortOffset (uint16_t port_offset); + +protected: + lldb::PlatformSP m_platform_sp; + + PortMap m_port_map; + uint16_t m_port_offset; + + PacketResult + Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qProcessInfo (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qGetWorkingDir (StringExtractorGDBRemote &packet); + + PacketResult + Handle_QSetWorkingDir (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qC (StringExtractorGDBRemote &packet); + +private: + bool + DebugserverProcessReaped (lldb::pid_t pid); + + static bool + ReapDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, + int status); + + //------------------------------------------------------------------ + // For GDBRemoteCommunicationServerPlatform only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationServerPlatform); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServerPlatform_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp index 6d7eca1a0ced..f5f134e80d6a 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -33,6 +33,7 @@ using namespace lldb; using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // GDBRemoteRegisterContext constructor @@ -151,7 +152,7 @@ GDBRemoteRegisterContext::PrivateSetRegisterValue (uint32_t reg, StringExtractor // Helper function for GDBRemoteRegisterContext::ReadRegisterBytes(). bool -GDBRemoteRegisterContext::GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, +GDBRemoteRegisterContext::GetPrimordialRegister(const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; @@ -265,7 +266,7 @@ GDBRemoteRegisterContext::WriteRegister (const RegisterInfo *reg_info, // Helper function for GDBRemoteRegisterContext::WriteRegisterBytes(). bool -GDBRemoteRegisterContext::SetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, +GDBRemoteRegisterContext::SetPrimordialRegister(const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { StreamString packet; @@ -316,7 +317,7 @@ GDBRemoteRegisterContext::SyncThreadState(Process *process) } bool -GDBRemoteRegisterContext::WriteRegisterBytes (const lldb_private::RegisterInfo *reg_info, DataExtractor &data, uint32_t data_offset) +GDBRemoteRegisterContext::WriteRegisterBytes (const RegisterInfo *reg_info, DataExtractor &data, uint32_t data_offset) { ExecutionContext exe_ctx (CalculateThread()); @@ -460,7 +461,7 @@ GDBRemoteRegisterContext::WriteRegisterBytes (const lldb_private::RegisterInfo * } bool -GDBRemoteRegisterContext::ReadAllRegisterValues (lldb_private::RegisterCheckpoint ®_checkpoint) +GDBRemoteRegisterContext::ReadAllRegisterValues (RegisterCheckpoint ®_checkpoint) { ExecutionContext exe_ctx (CalculateThread()); @@ -486,7 +487,7 @@ GDBRemoteRegisterContext::ReadAllRegisterValues (lldb_private::RegisterCheckpoin } bool -GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb_private::RegisterCheckpoint ®_checkpoint) +GDBRemoteRegisterContext::WriteAllRegisterValues (const RegisterCheckpoint ®_checkpoint) { uint32_t save_id = reg_checkpoint.GetID(); if (save_id != 0) @@ -676,15 +677,16 @@ GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data // This means buffer will be a little more than 2x larger than necessary but we resize // it down once we've extracted all hex ascii chars from the packet. DataBufferHeap buffer (G_packet_len, 0); + + const uint32_t bytes_extracted = response.GetHexBytes (buffer.GetBytes(), + buffer.GetByteSize(), + '\xcc'); + DataExtractor restore_data (buffer.GetBytes(), buffer.GetByteSize(), m_reg_data.GetByteOrder(), m_reg_data.GetAddressByteSize()); - - const uint32_t bytes_extracted = response.GetHexBytes ((void *)restore_data.GetDataStart(), - restore_data.GetByteSize(), - '\xcc'); - + if (bytes_extracted < restore_data.GetByteSize()) restore_data.SetData(restore_data.GetDataStart(), bytes_extracted, m_reg_data.GetByteOrder()); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h index b77381458914..117d280cc547 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -25,9 +25,13 @@ #include "GDBRemoteCommunicationClient.h" +class StringExtractor; + +namespace lldb_private { +namespace process_gdb_remote { + class ThreadGDBRemote; class ProcessGDBRemote; -class StringExtractor; class GDBRemoteDynamicRegisterInfo : public DynamicRegisterInfo @@ -47,7 +51,7 @@ public: }; -class GDBRemoteRegisterContext : public lldb_private::RegisterContext +class GDBRemoteRegisterContext : public RegisterContext { public: //------------------------------------------------------------------ @@ -64,52 +68,52 @@ public: //------------------------------------------------------------------ // Subclasses must override these functions //------------------------------------------------------------------ - virtual void - InvalidateAllRegisters (); + void + InvalidateAllRegisters () override; - virtual size_t - GetRegisterCount (); + size_t + GetRegisterCount () override; - virtual const lldb_private::RegisterInfo * - GetRegisterInfoAtIndex (size_t reg); + const RegisterInfo * + GetRegisterInfoAtIndex (size_t reg) override; - virtual size_t - GetRegisterSetCount (); + size_t + GetRegisterSetCount () override; - virtual const lldb_private::RegisterSet * - GetRegisterSet (size_t reg_set); + const RegisterSet * + GetRegisterSet (size_t reg_set) override; - virtual bool - ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + bool + ReadRegister (const RegisterInfo *reg_info, RegisterValue &value) override; - virtual bool - WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + bool + WriteRegister (const RegisterInfo *reg_info, const RegisterValue &value) override; - virtual bool - ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) override; - virtual bool - WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; - virtual bool - ReadAllRegisterValues (lldb_private::RegisterCheckpoint ®_checkpoint); + bool + ReadAllRegisterValues (RegisterCheckpoint ®_checkpoint) override; - virtual bool - WriteAllRegisterValues (const lldb_private::RegisterCheckpoint ®_checkpoint); + bool + WriteAllRegisterValues (const RegisterCheckpoint ®_checkpoint) override; - virtual uint32_t - ConvertRegisterKindToRegisterNumber (lldb::RegisterKind kind, uint32_t num); + uint32_t + ConvertRegisterKindToRegisterNumber (lldb::RegisterKind kind, uint32_t num) override; protected: friend class ThreadGDBRemote; bool - ReadRegisterBytes (const lldb_private::RegisterInfo *reg_info, - lldb_private::DataExtractor &data); + ReadRegisterBytes (const RegisterInfo *reg_info, + DataExtractor &data); bool - WriteRegisterBytes (const lldb_private::RegisterInfo *reg_info, - lldb_private::DataExtractor &data, + WriteRegisterBytes (const RegisterInfo *reg_info, + DataExtractor &data, uint32_t data_offset); bool @@ -130,7 +134,7 @@ protected: } void - SetRegisterIsValid (const lldb_private::RegisterInfo *reg_info, bool valid) + SetRegisterIsValid (const RegisterInfo *reg_info, bool valid) { if (reg_info) return SetRegisterIsValid (reg_info->kinds[lldb::eRegisterKindLLDB], valid); @@ -147,19 +151,19 @@ protected: } void - SyncThreadState(lldb_private::Process *process); // Assumes the sequence mutex has already been acquired. + SyncThreadState(Process *process); // Assumes the sequence mutex has already been acquired. GDBRemoteDynamicRegisterInfo &m_reg_info; std::vector<bool> m_reg_valid; - lldb_private::DataExtractor m_reg_data; + DataExtractor m_reg_data; bool m_read_all_at_once; private: // Helper function for ReadRegisterBytes(). - bool GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, + bool GetPrimordialRegister(const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm); // Helper function for WriteRegisterBytes(). - bool SetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, + bool SetPrimordialRegister(const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm); //------------------------------------------------------------------ @@ -168,4 +172,7 @@ private: DISALLOW_COPY_AND_ASSIGN (GDBRemoteRegisterContext); }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // lldb_GDBRemoteRegisterContext_h_ diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index cb0b4bb51007..5cb4da514a7f 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" #include "lldb/Host/Config.h" // C Includes @@ -24,8 +23,7 @@ // C++ Includes #include <algorithm> #include <map> - -// Other libraries and framework includes +#include <mutex> #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Interpreter/Args.h" @@ -41,18 +39,22 @@ #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" +#include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/TimeValue.h" +#include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" -#ifndef LLDB_DISABLE_PYTHON -#include "lldb/Interpreter/PythonDataObjects.h" -#endif +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" @@ -66,6 +68,7 @@ #include "Plugins/Process/Utility/FreeBSDSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/LinuxSignals.h" +#include "Plugins/Process/Utility/MipsLinuxSignals.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" #include "Utility/StringExtractorGDBRemote.h" @@ -74,6 +77,10 @@ #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" +#define DEBUGSERVER_BASENAME "debugserver" +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; namespace lldb { @@ -86,18 +93,13 @@ namespace lldb void DumpProcessGDBRemotePacketHistory (void *p, const char *path) { - lldb_private::StreamFile strm; - lldb_private::Error error (strm.GetFile().Open(path, lldb_private::File::eOpenOptionWrite | lldb_private::File::eOpenOptionCanCreate)); + StreamFile strm; + Error error (strm.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate)); if (error.Success()) ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory (strm); } } -#define DEBUGSERVER_BASENAME "debugserver" -using namespace lldb; -using namespace lldb_private; - - namespace { static PropertyDefinition @@ -171,6 +173,107 @@ namespace { } // anonymous namespace end +class ProcessGDBRemote::GDBLoadedModuleInfoList +{ +public: + + class LoadedModuleInfo + { + public: + + enum e_data_point + { + e_has_name = 0, + e_has_base , + e_has_dynamic , + e_has_link_map , + e_num + }; + + LoadedModuleInfo () + { + for (uint32_t i = 0; i < e_num; ++i) + m_has[i] = false; + }; + + void set_name (const std::string & name) + { + m_name = name; + m_has[e_has_name] = true; + } + bool get_name (std::string & out) const + { + out = m_name; + return m_has[e_has_name]; + } + + void set_base (const lldb::addr_t base) + { + m_base = base; + m_has[e_has_base] = true; + } + bool get_base (lldb::addr_t & out) const + { + out = m_base; + return m_has[e_has_base]; + } + + void set_link_map (const lldb::addr_t addr) + { + m_link_map = addr; + m_has[e_has_link_map] = true; + } + bool get_link_map (lldb::addr_t & out) const + { + out = m_link_map; + return m_has[e_has_link_map]; + } + + void set_dynamic (const lldb::addr_t addr) + { + m_dynamic = addr; + m_has[e_has_dynamic] = true; + } + bool get_dynamic (lldb::addr_t & out) const + { + out = m_dynamic; + return m_has[e_has_dynamic]; + } + + bool has_info (e_data_point datum) + { + assert (datum < e_num); + return m_has[datum]; + } + + protected: + + bool m_has[e_num]; + std::string m_name; + lldb::addr_t m_link_map; + lldb::addr_t m_base; + lldb::addr_t m_dynamic; + }; + + GDBLoadedModuleInfoList () + : m_list () + , m_link_map (LLDB_INVALID_ADDRESS) + {} + + void add (const LoadedModuleInfo & mod) + { + m_list.push_back (mod); + } + + void clear () + { + m_list.clear (); + } + + std::vector<LoadedModuleInfo> m_list; + lldb::addr_t m_link_map; +}; + // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing // it to debugserver. @@ -200,7 +303,7 @@ get_random_port () } #endif -lldb_private::ConstString +ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); @@ -268,14 +371,14 @@ ProcessGDBRemote::CanDebug (Target &target, bool plugin_specified_by_name) ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : Process (target, listener), m_flags (0), - m_gdb_comm(false), + m_gdb_comm (), m_debugserver_pid (LLDB_INVALID_PROCESS_ID), - m_last_stop_packet (), m_last_stop_packet_mutex (Mutex::eMutexTypeNormal), m_register_info (), m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), m_thread_ids (), + m_threads_info_sp (), m_continue_c_tids (), m_continue_C_tids (), m_continue_s_tids (), @@ -287,7 +390,8 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : m_waiting_for_attach (false), m_destroy_tried_resuming (false), m_command_sp (), - m_breakpoint_pc_offset (0) + m_breakpoint_pc_offset (0), + m_initial_tid (LLDB_INVALID_THREAD_ID) { m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); @@ -335,44 +439,69 @@ ProcessGDBRemote::GetPluginVersion() bool ProcessGDBRemote::ParsePythonTargetDefinition(const FileSpec &target_definition_fspec) { -#ifndef LLDB_DISABLE_PYTHON ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); Error error; - lldb::ScriptInterpreterObjectSP module_object_sp (interpreter->LoadPluginModule(target_definition_fspec, error)); + StructuredData::ObjectSP module_object_sp(interpreter->LoadPluginModule(target_definition_fspec, error)); if (module_object_sp) { - lldb::ScriptInterpreterObjectSP target_definition_sp (interpreter->GetDynamicSettings(module_object_sp, - &GetTarget(), - "gdb-server-target-definition", - error)); - - PythonDictionary target_dict(target_definition_sp); + StructuredData::DictionarySP target_definition_sp( + interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), "gdb-server-target-definition", error)); - if (target_dict) + if (target_definition_sp) { - PythonDictionary host_info_dict (target_dict.GetItemForKey("host-info")); - if (host_info_dict) + StructuredData::ObjectSP target_object(target_definition_sp->GetValueForKey("host-info")); + if (target_object) { - ArchSpec host_arch (host_info_dict.GetItemForKeyAsString(PythonString("triple"))); - - if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) + if (auto host_info_dict = target_object->GetAsDictionary()) { - GetTarget().SetArchitecture(host_arch); + StructuredData::ObjectSP triple_value = host_info_dict->GetValueForKey("triple"); + if (auto triple_string_value = triple_value->GetAsString()) + { + std::string triple_string = triple_string_value->GetValue(); + ArchSpec host_arch(triple_string.c_str()); + if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) + { + GetTarget().SetArchitecture(host_arch); + } + } } - } - m_breakpoint_pc_offset = target_dict.GetItemForKeyAsInteger("breakpoint-pc-offset", 0); + m_breakpoint_pc_offset = 0; + StructuredData::ObjectSP breakpoint_pc_offset_value = target_definition_sp->GetValueForKey("breakpoint-pc-offset"); + if (breakpoint_pc_offset_value) + { + if (auto breakpoint_pc_int_value = breakpoint_pc_offset_value->GetAsInteger()) + m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); + } - if (m_register_info.SetRegisterInfo (target_dict, GetTarget().GetArchitecture().GetByteOrder()) > 0) + if (m_register_info.SetRegisterInfo(*target_definition_sp, GetTarget().GetArchitecture()) > 0) { return true; } } } -#endif return false; } +static size_t +SplitCommaSeparatedRegisterNumberString(const llvm::StringRef &comma_separated_regiter_numbers, std::vector<uint32_t> ®nums, int base) +{ + regnums.clear(); + std::pair<llvm::StringRef, llvm::StringRef> value_pair; + value_pair.second = comma_separated_regiter_numbers; + do + { + value_pair = value_pair.second.split(','); + if (!value_pair.first.empty()) + { + uint32_t reg = StringConvert::ToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, base); + if (reg != LLDB_INVALID_REGNUM) + regnums.push_back (reg); + } + } while (!value_pair.second.empty()); + return regnums.size(); +} + void ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) @@ -380,8 +509,34 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) if (!force && m_register_info.GetNumRegisters() > 0) return; - char packet[128]; m_register_info.Clear(); + + // Check if qHostInfo specified a specific packet timeout for this connection. + // If so then lets update our setting so the user knows what the timeout is + // and can see it. + const uint32_t host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); + if (host_packet_timeout) + { + GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); + } + + // Register info search order: + // 1 - Use the target definition python file if one is specified. + // 2 - If the target definition doesn't have any of the info from the target.xml (registers) then proceed to read the target.xml. + // 3 - Fall back on the qRegisterInfo packets. + + FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile (); + if (target_definition_fspec) + { + // See if we can get register definitions from a python file + if (ParsePythonTargetDefinition (target_definition_fspec)) + return; + } + + if (GetGDBServerRegisterInfo ()) + return; + + char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; @@ -496,33 +651,11 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) } else if (name.compare("container-regs") == 0) { - std::pair<llvm::StringRef, llvm::StringRef> value_pair; - value_pair.second = value; - do - { - value_pair = value_pair.second.split(','); - if (!value_pair.first.empty()) - { - uint32_t reg = StringConvert::ToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16); - if (reg != LLDB_INVALID_REGNUM) - value_regs.push_back (reg); - } - } while (!value_pair.second.empty()); + SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); } else if (name.compare("invalidate-regs") == 0) { - std::pair<llvm::StringRef, llvm::StringRef> value_pair; - value_pair.second = value; - do - { - value_pair = value_pair.second.split(','); - if (!value_pair.first.empty()) - { - uint32_t reg = StringConvert::ToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16); - if (reg != LLDB_INVALID_REGNUM) - invalidate_regs.push_back (reg); - } - } while (!value_pair.second.empty()); + SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); } } @@ -553,26 +686,10 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) } } - // Check if qHostInfo specified a specific packet timeout for this connection. - // If so then lets update our setting so the user knows what the timeout is - // and can see it. - const uint32_t host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); - if (host_packet_timeout) + if (m_register_info.GetNumRegisters() > 0) { - GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); - } - - - if (reg_num == 0) - { - FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile (); - - if (target_definition_fspec) - { - // See if we can get register definitions from a python file - if (ParsePythonTargetDefinition (target_definition_fspec)) - return; - } + m_register_info.Finalize(GetTarget().GetArchitecture()); + return; } // We didn't get anything if the accumulated reg_num is zero. See if we are @@ -580,7 +697,7 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) // updated debugserver down on the devices. // On the other hand, if the accumulated reg_num is positive, see if we can // add composite registers to the existing primordial ones. - bool from_scratch = (reg_num == 0); + bool from_scratch = (m_register_info.GetNumRegisters() == 0); const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); @@ -606,7 +723,7 @@ ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) } // At this point, we can finalize our register info. - m_register_info.Finalize (); + m_register_info.Finalize (GetTarget().GetArchitecture()); } Error @@ -655,8 +772,15 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) // We have a valid process SetID (pid); GetThreadList(); - if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false) == GDBRemoteCommunication::PacketResult::Success) + StringExtractorGDBRemote response; + if (m_gdb_comm.GetStopReply(response)) { + SetLastStopPacket(response); + + // '?' Packets must be handled differently in non-stop mode + if (GetTarget().GetNonStopModeEnabled()) + HandleStopReplySequence(); + if (!m_target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) @@ -669,7 +793,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) } } - const StateType state = SetThreadStopInfo (m_last_stop_packet); + const StateType state = SetThreadStopInfo (response); if (state == eStateStopped) { SetPrivateState (state); @@ -703,7 +827,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) // FIXME Add a gdb-remote packet to discover dynamically. if (error.Success ()) { - const ArchSpec arch_spec = GetTarget ().GetArchitecture (); + const ArchSpec arch_spec = m_gdb_comm.GetHostArchitecture(); if (arch_spec.IsValid ()) { if (log) @@ -712,7 +836,10 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) switch (arch_spec.GetTriple ().getOS ()) { case llvm::Triple::Linux: - SetUnixSignals (UnixSignalsSP (new process_linux::LinuxSignals ())); + if (arch_spec.GetTriple ().getArch () == llvm::Triple::mips64 || arch_spec.GetTriple ().getArch () == llvm::Triple::mips64el) + SetUnixSignals (UnixSignalsSP (new process_linux::MipsLinuxSignals ())); + else + SetUnixSignals (UnixSignalsSP (new process_linux::LinuxSignals ())); if (log) log->Printf ("ProcessGDBRemote::%s using Linux unix signals type for pid %" PRIu64, __FUNCTION__, GetID ()); break; @@ -756,43 +883,55 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) log->Printf ("ProcessGDBRemote::%s() entered", __FUNCTION__); uint32_t launch_flags = launch_info.GetFlags().Get(); - const char *stdin_path = NULL; - const char *stdout_path = NULL; - const char *stderr_path = NULL; - const char *working_dir = launch_info.GetWorkingDirectory(); + FileSpec stdin_file_spec{}; + FileSpec stdout_file_spec{}; + FileSpec stderr_file_spec{}; + FileSpec working_dir = launch_info.GetWorkingDirectory(); const FileAction *file_action; file_action = launch_info.GetFileActionForFD (STDIN_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) - stdin_path = file_action->GetPath(); + stdin_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) - stdout_path = file_action->GetPath(); + stdout_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD (STDERR_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) - stderr_path = file_action->GetPath(); + stderr_file_spec = file_action->GetFileSpec(); } if (log) { - if (stdin_path || stdout_path || stderr_path) - log->Printf ("ProcessGDBRemote::%s provided with STDIO paths via launch_info: stdin=%s, stdout=%s, stdout=%s", + if (stdin_file_spec || stdout_file_spec || stderr_file_spec) + log->Printf ("ProcessGDBRemote::%s provided with STDIO paths via launch_info: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, - stdin_path ? stdin_path : "<null>", - stdout_path ? stdout_path : "<null>", - stderr_path ? stderr_path : "<null>"); + stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", + stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", + stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); else log->Printf ("ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); } + const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; + if (stdin_file_spec || disable_stdio) + { + // the inferior will be reading stdin from the specified file + // or stdio is completely disabled + m_stdin_forward = false; + } + else + { + m_stdin_forward = true; + } + // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); @@ -811,64 +950,58 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) lldb_utility::PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; - // If the debugserver is local and we aren't disabling STDIO, lets use - // a pseudo terminal to instead of relying on the 'O' packets for stdio - // since 'O' packets can really slow down debugging if the inferior - // does a lot of output. PlatformSP platform_sp (m_target.GetPlatform()); - if (platform_sp && platform_sp->IsHost() && !disable_stdio) + if (disable_stdio) + { + // set to /dev/null unless redirected to a file above + if (!stdin_file_spec) + stdin_file_spec.SetFile("/dev/null", false); + if (!stdout_file_spec) + stdout_file_spec.SetFile("/dev/null", false); + if (!stderr_file_spec) + stderr_file_spec.SetFile("/dev/null", false); + } + else if (platform_sp && platform_sp->IsHost()) { - const char *slave_name = NULL; - if (stdin_path == NULL || stdout_path == NULL || stderr_path == NULL) + // If the debugserver is local and we aren't disabling STDIO, lets use + // a pseudo terminal to instead of relying on the 'O' packets for stdio + // since 'O' packets can really slow down debugging if the inferior + // does a lot of output. + if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && + pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) { - if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) - slave_name = pty.GetSlaveName (NULL, 0); - } - if (stdin_path == NULL) - stdin_path = slave_name; + FileSpec slave_name{pty.GetSlaveName(NULL, 0), false}; - if (stdout_path == NULL) - stdout_path = slave_name; + if (!stdin_file_spec) + stdin_file_spec = slave_name; - if (stderr_path == NULL) - stderr_path = slave_name; + if (!stdout_file_spec) + stdout_file_spec = slave_name; + if (!stderr_file_spec) + stderr_file_spec = slave_name; + } if (log) - log->Printf ("ProcessGDBRemote::%s adjusted STDIO paths for local platform (IsHost() is true) using slave: stdin=%s, stdout=%s, stdout=%s", + log->Printf ("ProcessGDBRemote::%s adjusted STDIO paths for local platform (IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, - stdin_path ? stdin_path : "<null>", - stdout_path ? stdout_path : "<null>", - stderr_path ? stderr_path : "<null>"); + stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", + stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", + stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); } - // Set STDIN to /dev/null if we want STDIO disabled or if either - // STDOUT or STDERR have been set to something and STDIN hasn't - if (disable_stdio || (stdin_path == NULL && (stdout_path || stderr_path))) - stdin_path = "/dev/null"; - - // Set STDOUT to /dev/null if we want STDIO disabled or if either - // STDIN or STDERR have been set to something and STDOUT hasn't - if (disable_stdio || (stdout_path == NULL && (stdin_path || stderr_path))) - stdout_path = "/dev/null"; - - // Set STDERR to /dev/null if we want STDIO disabled or if either - // STDIN or STDOUT have been set to something and STDERR hasn't - if (disable_stdio || (stderr_path == NULL && (stdin_path || stdout_path))) - stderr_path = "/dev/null"; - if (log) - log->Printf ("ProcessGDBRemote::%s final STDIO paths after all adjustments: stdin=%s, stdout=%s, stdout=%s", + log->Printf ("ProcessGDBRemote::%s final STDIO paths after all adjustments: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, - stdin_path ? stdin_path : "<null>", - stdout_path ? stdout_path : "<null>", - stderr_path ? stderr_path : "<null>"); + stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", + stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", + stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); - if (stdin_path) - m_gdb_comm.SetSTDIN (stdin_path); - if (stdout_path) - m_gdb_comm.SetSTDOUT (stdout_path); - if (stderr_path) - m_gdb_comm.SetSTDERR (stderr_path); + if (stdin_file_spec) + m_gdb_comm.SetSTDIN(stdin_file_spec); + if (stdout_file_spec) + m_gdb_comm.SetSTDOUT(stdout_file_spec); + if (stderr_file_spec) + m_gdb_comm.SetSTDERR(stderr_file_spec); m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError); @@ -879,7 +1012,7 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket (launch_event_data); - if (working_dir && working_dir[0]) + if (working_dir) { m_gdb_comm.SetWorkingDir (working_dir); } @@ -897,27 +1030,29 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) } } - const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (10); - int arg_packet_err = m_gdb_comm.SendArgumentsPacket (launch_info); - if (arg_packet_err == 0) { - std::string error_str; - if (m_gdb_comm.GetLaunchSuccess (error_str)) + // Scope for the scoped timeout object + GDBRemoteCommunication::ScopedTimeout timeout (m_gdb_comm, 10); + + int arg_packet_err = m_gdb_comm.SendArgumentsPacket (launch_info); + if (arg_packet_err == 0) { - SetID (m_gdb_comm.GetCurrentProcessID ()); + std::string error_str; + if (m_gdb_comm.GetLaunchSuccess (error_str)) + { + SetID (m_gdb_comm.GetCurrentProcessID ()); + } + else + { + error.SetErrorString (error_str.c_str()); + } } else { - error.SetErrorString (error_str.c_str()); + error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } } - else - { - error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); - } - - m_gdb_comm.SetPacketTimeout (old_packet_timeout); - + if (GetID() == LLDB_INVALID_PROCESS_ID) { if (log) @@ -926,23 +1061,29 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) return error; } - if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false) == GDBRemoteCommunication::PacketResult::Success) + StringExtractorGDBRemote response; + if (m_gdb_comm.GetStopReply(response)) { - if (!m_target.GetArchitecture().IsValid()) + SetLastStopPacket(response); + // '?' Packets must be handled differently in non-stop mode + if (GetTarget().GetNonStopModeEnabled()) + HandleStopReplySequence(); + + const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); + + if (process_arch.IsValid()) { - if (m_gdb_comm.GetProcessArchitecture().IsValid()) - { - m_target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); - } - else - { - m_target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); - } + m_target.MergeArchitecture(process_arch); + } + else + { + const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); + if (host_arch.IsValid()) + m_target.MergeArchitecture(host_arch); } - SetPrivateState (SetThreadStopInfo (m_last_stop_packet)); + SetPrivateState (SetThreadStopInfo (response)); - m_stdio_disable = disable_stdio; if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) @@ -1015,6 +1156,12 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) return error; } + + // Start the communications read thread so all incoming data can be + // parsed into packets and queued as they arrive. + if (GetTarget().GetNonStopModeEnabled()) + m_gdb_comm.StartReadThread(); + // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the @@ -1027,12 +1174,23 @@ ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) error.SetErrorString("not connected to remote gdb server"); return error; } + + // Send $QNonStop:1 packet on startup if required + if (GetTarget().GetNonStopModeEnabled()) + GetTarget().SetNonStopModeEnabled (m_gdb_comm.SetNonStopMode(true)); + + m_gdb_comm.GetEchoSupported (); m_gdb_comm.GetThreadSuffixSupported (); m_gdb_comm.GetListThreadsInStopReplySupported (); m_gdb_comm.GetHostInfo (); m_gdb_comm.GetVContSupported ('c'); m_gdb_comm.GetVAttachOrWaitSupported(); - + + // Ask the remote server for the default thread id + if (GetTarget().GetNonStopModeEnabled()) + m_gdb_comm.GetDefaultThreadId(m_initial_tid); + + size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { @@ -1081,7 +1239,7 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) if (process_arch.IsValid()) { - ArchSpec &target_arch = GetTarget().GetArchitecture(); + const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { if (log) @@ -1111,20 +1269,23 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) { // Fill in what is missing in the triple const llvm::Triple &remote_triple = process_arch.GetTriple(); - llvm::Triple &target_triple = target_arch.GetTriple(); - if (target_triple.getVendorName().size() == 0) + llvm::Triple new_target_triple = target_arch.GetTriple(); + if (new_target_triple.getVendorName().size() == 0) { - target_triple.setVendor (remote_triple.getVendor()); + new_target_triple.setVendor (remote_triple.getVendor()); - if (target_triple.getOSName().size() == 0) + if (new_target_triple.getOSName().size() == 0) { - target_triple.setOS (remote_triple.getOS()); + new_target_triple.setOS (remote_triple.getOS()); - if (target_triple.getEnvironmentName().size() == 0) - target_triple.setEnvironment (remote_triple.getEnvironment()); + if (new_target_triple.getEnvironmentName().size() == 0) + new_target_triple.setEnvironment (remote_triple.getEnvironment()); } - } + ArchSpec new_target_arch = target_arch; + new_target_arch.SetTriple(new_target_triple); + GetTarget().SetArchitecture(new_target_arch); + } } if (log) @@ -1151,13 +1312,6 @@ ProcessGDBRemote::DidLaunch () } Error -ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid) -{ - ProcessAttachInfo attach_info; - return DoAttachToProcessWithID(attach_pid, attach_info); -} - -Error ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); @@ -1255,12 +1409,11 @@ ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const Pro return error; } - -bool -ProcessGDBRemote::SetExitStatus (int exit_status, const char *cstr) +void +ProcessGDBRemote::DidExit () { + // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); - return Process::SetExitStatus (exit_status, cstr); } void @@ -1301,11 +1454,12 @@ ProcessGDBRemote::DoResume () bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport ()) { - if (m_continue_c_tids.size() == num_threads || + if (!GetTarget().GetNonStopModeEnabled() && + (m_continue_c_tids.size() == num_threads || (m_continue_c_tids.empty() && m_continue_C_tids.empty() && m_continue_s_tids.empty() && - m_continue_S_tids.empty())) + m_continue_S_tids.empty()))) { // All threads are continuing, just send a "c" packet continue_packet.PutCString ("c"); @@ -1443,7 +1597,18 @@ ProcessGDBRemote::DoResume () { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); - continue_packet.PutChar ('s'); + + // If in Non-Stop-Mode use vCont when stepping + if (GetTarget().GetNonStopModeEnabled()) + { + if (m_gdb_comm.GetVContSupported('s')) + continue_packet.PutCString("vCont;s"); + else + continue_packet.PutChar('s'); + } + else + continue_packet.PutChar('s'); + continue_packet_error = false; } else if (num_continue_c_tids == 0 && @@ -1533,16 +1698,112 @@ ProcessGDBRemote::DoResume () } void +ProcessGDBRemote::HandleStopReplySequence () +{ + while(true) + { + // Send vStopped + StringExtractorGDBRemote response; + m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); + + // OK represents end of signal list + if (response.IsOKResponse()) + break; + + // If not OK or a normal packet we have a problem + if (!response.IsNormalResponse()) + break; + + SetLastStopPacket(response); + } +} + +void ProcessGDBRemote::ClearThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); } +size_t +ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue (std::string &value) +{ + m_thread_ids.clear(); + size_t comma_pos; + lldb::tid_t tid; + while ((comma_pos = value.find(',')) != std::string::npos) + { + value[comma_pos] = '\0'; + // thread in big endian hex + tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); + if (tid != LLDB_INVALID_THREAD_ID) + m_thread_ids.push_back (tid); + value.erase(0, comma_pos + 1); + } + tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); + if (tid != LLDB_INVALID_THREAD_ID) + m_thread_ids.push_back (tid); + return m_thread_ids.size(); +} + bool ProcessGDBRemote::UpdateThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); + + if (m_threads_info_sp) + { + // If we have the JSON threads info, we can get the thread list from that + StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray(); + if (thread_infos && thread_infos->GetSize() > 0) + { + m_thread_ids.clear(); + thread_infos->ForEach([this](StructuredData::Object* object) -> bool { + StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); + if (thread_dict) + { + // Set the thread stop info from the JSON dictionary + SetThreadStopInfo (thread_dict); + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid)) + m_thread_ids.push_back(tid); + } + return true; // Keep iterating through all thread_info objects + }); + } + if (!m_thread_ids.empty()) + return true; + } + else + { + // See if we can get the thread IDs from the current stop reply packets + // that might contain a "threads" key/value pair + + // Lock the thread stack while we access it + Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); + // Get the number of stop packets on the stack + int nItems = m_stop_packet_stack.size(); + // Iterate over them + for (int i = 0; i < nItems; i++) + { + // Get the thread stop info + StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; + const std::string &stop_info_str = stop_info.GetStringRef(); + const size_t threads_pos = stop_info_str.find(";threads:"); + if (threads_pos != std::string::npos) + { + const size_t start = threads_pos + strlen(";threads:"); + const size_t end = stop_info_str.find(';', start); + if (end != std::string::npos) + { + std::string value = stop_info_str.substr(start, end - start); + if (UpdateThreadIDsFromStopReplyThreadsValue(value)) + return true; + } + } + } + } + bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs (m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) @@ -1614,6 +1875,423 @@ ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new return true; } +bool +ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread) +{ + // See if we got thread stop infos for all threads via the "jThreadsInfo" packet + if (m_threads_info_sp) + { + StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray(); + if (thread_infos) + { + lldb::tid_t tid; + const size_t n = thread_infos->GetSize(); + for (size_t i=0; i<n; ++i) + { + StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); + if (thread_dict) + { + if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid, LLDB_INVALID_THREAD_ID)) + { + if (tid == thread->GetID()) + return SetThreadStopInfo(thread_dict); + } + } + } + } + } + + // Fall back to using the qThreadStopInfo packet + StringExtractorGDBRemote stop_packet; + if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) + return SetThreadStopInfo (stop_packet) == eStateStopped; + return false; +} + + +ThreadSP +ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, + ExpeditedRegisterMap &expedited_register_map, + uint8_t signo, + const std::string &thread_name, + const std::string &reason, + const std::string &description, + uint32_t exc_type, + const std::vector<addr_t> &exc_data, + addr_t thread_dispatch_qaddr, + bool queue_vars_valid, // Set to true if queue_name, queue_kind and queue_serial are valid + std::string &queue_name, + QueueKind queue_kind, + uint64_t queue_serial) +{ + ThreadSP thread_sp; + if (tid != LLDB_INVALID_THREAD_ID) + { + // Scope for "locker" below + { + // m_thread_list_real does have its own mutex, but we need to + // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) + // and the m_thread_list_real.AddThread(...) so it doesn't change on us + Mutex::Locker locker (m_thread_list_real.GetMutex ()); + thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); + + if (!thread_sp) + { + // Create the thread if we need to + thread_sp.reset (new ThreadGDBRemote (*this, tid)); + m_thread_list_real.AddThread(thread_sp); + } + } + + if (thread_sp) + { + ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get()); + gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); + + for (const auto &pair : expedited_register_map) + { + StringExtractor reg_value_extractor; + reg_value_extractor.GetStringRef() = pair.second; + gdb_thread->PrivateSetRegisterValue (pair.first, reg_value_extractor); + } + + // Clear the stop info just in case we don't set it to anything + thread_sp->SetStopInfo (StopInfoSP()); + thread_sp->SetName (thread_name.empty() ? NULL : thread_name.c_str()); + + gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); + // Check if the GDB server was able to provide the queue name, kind and serial number + if (queue_vars_valid) + gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial); + else + gdb_thread->ClearQueueInfo(); + + + if (exc_type != 0) + { + const size_t exc_data_size = exc_data.size(); + + thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp, + exc_type, + exc_data_size, + exc_data_size >= 1 ? exc_data[0] : 0, + exc_data_size >= 2 ? exc_data[1] : 0, + exc_data_size >= 3 ? exc_data[2] : 0)); + } + else + { + bool handled = false; + bool did_exec = false; + if (!reason.empty()) + { + if (reason.compare("trace") == 0) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); + handled = true; + } + else if (reason.compare("breakpoint") == 0) + { + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp) + { + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + handled = true; + if (bp_site_sp->ValidForThisThread (thread_sp.get())) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); + } + else + { + StopInfoSP invalid_stop_info_sp; + thread_sp->SetStopInfo (invalid_stop_info_sp); + } + } + } + else if (reason.compare("trap") == 0) + { + // Let the trap just use the standard signal stop reason below... + } + else if (reason.compare("watchpoint") == 0) + { + StringExtractor desc_extractor(description.c_str()); + addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); + uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); + watch_id_t watch_id = LLDB_INVALID_WATCH_ID; + if (wp_addr != LLDB_INVALID_ADDRESS) + { + WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); + if (wp_sp) + { + wp_sp->SetHardwareIndex(wp_index); + watch_id = wp_sp->GetID(); + } + } + if (watch_id == LLDB_INVALID_WATCH_ID) + { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); + if (log) log->Printf ("failed to find watchpoint"); + } + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); + handled = true; + } + else if (reason.compare("exception") == 0) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); + handled = true; + } + else if (reason.compare("exec") == 0) + { + did_exec = true; + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); + handled = true; + } + } + + if (!handled && signo && did_exec == false) + { + if (signo == SIGTRAP) + { + // Currently we are going to assume SIGTRAP means we are either + // hitting a breakpoint or hardware single stepping. + handled = true; + addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; + lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + + if (bp_site_sp) + { + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + if (bp_site_sp->ValidForThisThread (thread_sp.get())) + { + if(m_breakpoint_pc_offset != 0) + thread_sp->GetRegisterContext()->SetPC(pc); + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); + } + else + { + StopInfoSP invalid_stop_info_sp; + thread_sp->SetStopInfo (invalid_stop_info_sp); + } + } + else + { + // If we were stepping then assume the stop was the result of the trace. If we were + // not stepping then report the SIGTRAP. + // FIXME: We are still missing the case where we single step over a trap instruction. + if (thread_sp->GetTemporaryResumeState() == eStateStepping) + thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); + else + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str())); + } + } + if (!handled) + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str())); + } + + if (!description.empty()) + { + lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); + if (stop_info_sp) + { + const char *stop_info_desc = stop_info_sp->GetDescription(); + if (!stop_info_desc || !stop_info_desc[0]) + stop_info_sp->SetDescription (description.c_str()); + } + else + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str())); + } + } + } + } + } + return thread_sp; +} + +StateType +ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict) +{ + static ConstString g_key_tid("tid"); + static ConstString g_key_name("name"); + static ConstString g_key_reason("reason"); + static ConstString g_key_metype("metype"); + static ConstString g_key_medata("medata"); + static ConstString g_key_qaddr("qaddr"); + static ConstString g_key_queue_name("qname"); + static ConstString g_key_queue_kind("qkind"); + static ConstString g_key_queue_serial("qserial"); + static ConstString g_key_registers("registers"); + static ConstString g_key_memory("memory"); + static ConstString g_key_address("address"); + static ConstString g_key_bytes("bytes"); + static ConstString g_key_description("description"); + + // Stop with signal and thread info + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + uint8_t signo = 0; + std::string value; + std::string thread_name; + std::string reason; + std::string description; + uint32_t exc_type = 0; + std::vector<addr_t> exc_data; + addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + ExpeditedRegisterMap expedited_register_map; + bool queue_vars_valid = false; + std::string queue_name; + QueueKind queue_kind = eQueueKindUnknown; + uint64_t queue_serial = 0; + // Iterate through all of the thread dictionary key/value pairs from the structured data dictionary + + thread_dict->ForEach([this, + &tid, + &expedited_register_map, + &thread_name, + &signo, + &reason, + &description, + &exc_type, + &exc_data, + &thread_dispatch_qaddr, + &queue_vars_valid, + &queue_name, + &queue_kind, + &queue_serial] + (ConstString key, StructuredData::Object* object) -> bool + { + if (key == g_key_tid) + { + // thread in big endian hex + tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); + } + else if (key == g_key_metype) + { + // exception type in big endian hex + exc_type = object->GetIntegerValue(0); + } + else if (key == g_key_medata) + { + // exception data in big endian hex + StructuredData::Array *array = object->GetAsArray(); + if (array) + { + array->ForEach([&exc_data](StructuredData::Object* object) -> bool { + exc_data.push_back(object->GetIntegerValue()); + return true; // Keep iterating through all array items + }); + } + } + else if (key == g_key_name) + { + thread_name = std::move(object->GetStringValue()); + } + else if (key == g_key_qaddr) + { + thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); + } + else if (key == g_key_queue_name) + { + queue_vars_valid = true; + queue_name = std::move(object->GetStringValue()); + } + else if (key == g_key_queue_kind) + { + std::string queue_kind_str = object->GetStringValue(); + if (queue_kind_str == "serial") + { + queue_vars_valid = true; + queue_kind = eQueueKindSerial; + } + else if (queue_kind_str == "concurrent") + { + queue_vars_valid = true; + queue_kind = eQueueKindConcurrent; + } + } + else if (key == g_key_queue_serial) + { + queue_serial = object->GetIntegerValue(0); + if (queue_serial != 0) + queue_vars_valid = true; + } + else if (key == g_key_reason) + { + reason = std::move(object->GetStringValue()); + } + else if (key == g_key_description) + { + description = std::move(object->GetStringValue()); + } + else if (key == g_key_registers) + { + StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); + + if (registers_dict) + { + registers_dict->ForEach([&expedited_register_map](ConstString key, StructuredData::Object* object) -> bool { + const uint32_t reg = StringConvert::ToUInt32 (key.GetCString(), UINT32_MAX, 10); + if (reg != UINT32_MAX) + expedited_register_map[reg] = std::move(object->GetStringValue()); + return true; // Keep iterating through all array items + }); + } + } + else if (key == g_key_memory) + { + StructuredData::Array *array = object->GetAsArray(); + if (array) + { + array->ForEach([this](StructuredData::Object* object) -> bool { + StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary(); + if (mem_cache_dict) + { + lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; + if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>("address", mem_cache_addr)) + { + if (mem_cache_addr != LLDB_INVALID_ADDRESS) + { + StringExtractor bytes; + if (mem_cache_dict->GetValueForKeyAsString("bytes", bytes.GetStringRef())) + { + bytes.SetFilePos(0); + + const size_t byte_size = bytes.GetStringRef().size()/2; + DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); + const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0); + if (bytes_copied == byte_size) + m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); + } + } + } + } + return true; // Keep iterating through all array items + }); + } + + } + return true; // Keep iterating through all dictionary key/value pairs + }); + + SetThreadStopInfo (tid, + expedited_register_map, + signo, + thread_name, + reason, + description, + exc_type, + exc_data, + thread_dispatch_qaddr, + queue_vars_valid, + queue_name, + queue_kind, + queue_serial); + + return eStateExited; +} StateType ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) @@ -1644,8 +2322,9 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) BuildDynamicRegisterInfo (true); } // Stop with signal and thread info + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; const uint8_t signo = stop_packet.GetHexU8(); - std::string name; + std::string key; std::string value; std::string thread_name; std::string reason; @@ -1653,48 +2332,29 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) uint32_t exc_type = 0; std::vector<addr_t> exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; - ThreadSP thread_sp; - ThreadGDBRemote *gdb_thread = NULL; - - while (stop_packet.GetNameColonValue(name, value)) + bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid + std::string queue_name; + QueueKind queue_kind = eQueueKindUnknown; + uint64_t queue_serial = 0; + ExpeditedRegisterMap expedited_register_map; + while (stop_packet.GetNameColonValue(key, value)) { - if (name.compare("metype") == 0) + if (key.compare("metype") == 0) { // exception type in big endian hex exc_type = StringConvert::ToUInt32 (value.c_str(), 0, 16); } - else if (name.compare("medata") == 0) + else if (key.compare("medata") == 0) { // exception data in big endian hex exc_data.push_back(StringConvert::ToUInt64 (value.c_str(), 0, 16)); } - else if (name.compare("thread") == 0) + else if (key.compare("thread") == 0) { // thread in big endian hex - lldb::tid_t tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); - // m_thread_list_real does have its own mutex, but we need to - // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) - // and the m_thread_list_real.AddThread(...) so it doesn't change on us - Mutex::Locker locker (m_thread_list_real.GetMutex ()); - thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); - - if (!thread_sp) - { - // Create the thread if we need to - thread_sp.reset (new ThreadGDBRemote (*this, tid)); - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); - if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) - log->Printf ("ProcessGDBRemote::%s Adding new thread: %p for thread ID: 0x%" PRIx64 ".\n", - __FUNCTION__, - static_cast<void*>(thread_sp.get()), - thread_sp->GetID()); - - m_thread_list_real.AddThread(thread_sp); - } - gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get()); - + tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); } - else if (name.compare("threads") == 0) + else if (key.compare("threads") == 0) { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); @@ -1716,7 +2376,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); } - else if (name.compare("hexname") == 0) + else if (key.compare("hexname") == 0) { StringExtractor name_extractor; // Swap "value" over into "name_extractor" @@ -1725,19 +2385,48 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) name_extractor.GetHexByteString (value); thread_name.swap (value); } - else if (name.compare("name") == 0) + else if (key.compare("name") == 0) { thread_name.swap (value); } - else if (name.compare("qaddr") == 0) + else if (key.compare("qaddr") == 0) { thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16); } - else if (name.compare("reason") == 0) + else if (key.compare("qname") == 0) + { + queue_vars_valid = true; + StringExtractor name_extractor; + // Swap "value" over into "name_extractor" + name_extractor.GetStringRef().swap(value); + // Now convert the HEX bytes into a string value + name_extractor.GetHexByteString (value); + queue_name.swap (value); + } + else if (key.compare("qkind") == 0) + { + if (value == "serial") + { + queue_vars_valid = true; + queue_kind = eQueueKindSerial; + } + else if (value == "concurrent") + { + queue_vars_valid = true; + queue_kind = eQueueKindConcurrent; + } + } + else if (key.compare("qserial") == 0) + { + queue_serial = StringConvert::ToUInt64 (value.c_str(), 0, 0); + if (queue_serial != 0) + queue_vars_valid = true; + } + else if (key.compare("reason") == 0) { reason.swap(value); } - else if (name.compare("description") == 0) + else if (key.compare("description") == 0) { StringExtractor desc_extractor; // Swap "value" over into "name_extractor" @@ -1746,34 +2435,61 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) desc_extractor.GetHexByteString (value); description.swap(value); } - else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1])) + else if (key.compare("memory") == 0) { - // We have a register number that contains an expedited - // register value. Lets supply this register to our thread - // so it won't have to go and read it. - if (gdb_thread) + // Expedited memory. GDB servers can choose to send back expedited memory + // that can populate the L1 memory cache in the process so that things like + // the frame pointer backchain can be expedited. This will help stack + // backtracing be more efficient by not having to send as many memory read + // requests down the remote GDB server. + + // Key/value pair format: memory:<addr>=<bytes>; + // <addr> is a number whose base will be interpreted by the prefix: + // "0x[0-9a-fA-F]+" for hex + // "0[0-7]+" for octal + // "[1-9]+" for decimal + // <bytes> is native endian ASCII hex bytes just like the register values + llvm::StringRef value_ref(value); + std::pair<llvm::StringRef, llvm::StringRef> pair; + pair = value_ref.split('='); + if (!pair.first.empty() && !pair.second.empty()) { - uint32_t reg = StringConvert::ToUInt32 (name.c_str(), UINT32_MAX, 16); - - if (reg != UINT32_MAX) + std::string addr_str(pair.first.str()); + const lldb::addr_t mem_cache_addr = StringConvert::ToUInt64(addr_str.c_str(), LLDB_INVALID_ADDRESS, 0); + if (mem_cache_addr != LLDB_INVALID_ADDRESS) { - StringExtractor reg_value_extractor; - // Swap "value" over into "reg_value_extractor" - reg_value_extractor.GetStringRef().swap(value); - if (!gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor)) - { - Host::SetCrashDescriptionWithFormat("Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s'", - name.c_str(), - reg, - reg, - reg_value_extractor.GetStringRef().c_str(), - stop_packet.GetStringRef().c_str()); - } + StringExtractor bytes; + bytes.GetStringRef() = std::move(pair.second.str()); + const size_t byte_size = bytes.GetStringRef().size()/2; + DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); + const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0); + if (bytes_copied == byte_size) + m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } + else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) + { + uint32_t reg = StringConvert::ToUInt32 (key.c_str(), UINT32_MAX, 16); + if (reg != UINT32_MAX) + expedited_register_map[reg] = std::move(value); + } } + ThreadSP thread_sp = SetThreadStopInfo (tid, + expedited_register_map, + signo, + thread_name, + reason, + description, + exc_type, + exc_data, + thread_dispatch_qaddr, + queue_vars_valid, + queue_name, + queue_kind, + queue_serial); + // If the response is old style 'S' packet which does not provide us with thread information // then update the thread list and choose the first one. if (!thread_sp) @@ -1784,157 +2500,9 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) { Mutex::Locker locker (m_thread_list_real.GetMutex ()); thread_sp = m_thread_list_real.FindThreadByProtocolID (m_thread_ids.front (), false); - if (thread_sp) - gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get ()); } } - if (thread_sp) - { - // Clear the stop info just in case we don't set it to anything - thread_sp->SetStopInfo (StopInfoSP()); - - gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); - gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str()); - if (exc_type != 0) - { - const size_t exc_data_size = exc_data.size(); - - thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp, - exc_type, - exc_data_size, - exc_data_size >= 1 ? exc_data[0] : 0, - exc_data_size >= 2 ? exc_data[1] : 0, - exc_data_size >= 3 ? exc_data[2] : 0)); - } - else - { - bool handled = false; - bool did_exec = false; - if (!reason.empty()) - { - if (reason.compare("trace") == 0) - { - thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); - handled = true; - } - else if (reason.compare("breakpoint") == 0) - { - addr_t pc = thread_sp->GetRegisterContext()->GetPC(); - lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); - if (bp_site_sp) - { - // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, - // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that - // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. - handled = true; - if (bp_site_sp->ValidForThisThread (thread_sp.get())) - { - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); - } - else - { - StopInfoSP invalid_stop_info_sp; - thread_sp->SetStopInfo (invalid_stop_info_sp); - } - } - } - else if (reason.compare("trap") == 0) - { - // Let the trap just use the standard signal stop reason below... - } - else if (reason.compare("watchpoint") == 0) - { - StringExtractor desc_extractor(description.c_str()); - addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); - uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); - watch_id_t watch_id = LLDB_INVALID_WATCH_ID; - if (wp_addr != LLDB_INVALID_ADDRESS) - { - WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); - if (wp_sp) - { - wp_sp->SetHardwareIndex(wp_index); - watch_id = wp_sp->GetID(); - } - } - if (watch_id == LLDB_INVALID_WATCH_ID) - { - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); - if (log) log->Printf ("failed to find watchpoint"); - } - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); - handled = true; - } - else if (reason.compare("exception") == 0) - { - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); - handled = true; - } - else if (reason.compare("exec") == 0) - { - did_exec = true; - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); - handled = true; - } - } - - if (!handled && signo && did_exec == false) - { - if (signo == SIGTRAP) - { - // Currently we are going to assume SIGTRAP means we are either - // hitting a breakpoint or hardware single stepping. - handled = true; - addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; - lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); - - if (bp_site_sp) - { - // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, - // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that - // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. - if (bp_site_sp->ValidForThisThread (thread_sp.get())) - { - if(m_breakpoint_pc_offset != 0) - thread_sp->GetRegisterContext()->SetPC(pc); - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); - } - else - { - StopInfoSP invalid_stop_info_sp; - thread_sp->SetStopInfo (invalid_stop_info_sp); - } - } - else - { - // If we were stepping then assume the stop was the result of the trace. If we were - // not stepping then report the SIGTRAP. - // FIXME: We are still missing the case where we single step over a trap instruction. - if (thread_sp->GetTemporaryResumeState() == eStateStepping) - thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); - else - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo)); - } - } - if (!handled) - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo)); - } - - if (!description.empty()) - { - lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); - if (stop_info_sp) - { - stop_info_sp->SetDescription (description.c_str()); - } - else - { - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str())); - } - } - } - } return eStateStopped; } break; @@ -1958,7 +2526,25 @@ ProcessGDBRemote::RefreshStateAfterStop () // Set the thread stop info. It might have a "threads" key whose value is // a list of all thread IDs in the current process, so m_thread_ids might // get set. - SetThreadStopInfo (m_last_stop_packet); + + // Scope for the lock + { + // Lock the thread stack while we access it + Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); + // Get the number of stop packets on the stack + int nItems = m_stop_packet_stack.size(); + // Iterate over them + for (int i = 0; i < nItems; i++) + { + // Get the thread stop info + StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; + // Process thread stop info + SetThreadStopInfo(stop_info); + } + // Clear the thread stop stack + m_stop_packet_stack.clear(); + } + // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { @@ -1966,6 +2552,18 @@ ProcessGDBRemote::RefreshStateAfterStop () UpdateThreadIDList(); } + // If we have queried for a default thread id + if (m_initial_tid != LLDB_INVALID_THREAD_ID) + { + m_thread_list.SetSelectedThreadByID(m_initial_tid); + m_initial_tid = LLDB_INVALID_THREAD_ID; + } + + // Fetch the threads via an efficient packet that gets stop infos for all threads + // only if we have more than one thread + if (m_thread_ids.size() > 1) + m_threads_info_sp = m_gdb_comm.GetThreadsInfo(); + // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); @@ -2138,7 +2736,7 @@ ProcessGDBRemote::DoDestroy () } } Resume (); - return Destroy(); + return Destroy(false); } } } @@ -2152,10 +2750,9 @@ ProcessGDBRemote::DoDestroy () { if (m_public_state.GetValue() != eStateAttaching) { - StringExtractorGDBRemote response; bool send_async = true; - const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (3); + GDBRemoteCommunication::ScopedTimeout (m_gdb_comm, 3); if (m_gdb_comm.SendPacketAndWaitForResponse("k", 1, response, send_async) == GDBRemoteCommunication::PacketResult::Success) { @@ -2199,8 +2796,6 @@ ProcessGDBRemote::DoDestroy () log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } - - m_gdb_comm.SetPacketTimeout(old_packet_timeout); } else { @@ -2226,7 +2821,6 @@ ProcessGDBRemote::DoDestroy () void ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) { - lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex); const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { @@ -2237,9 +2831,18 @@ ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo (true); - m_gdb_comm.ResetDiscoverableSettings(); + m_gdb_comm.ResetDiscoverableSettings (did_exec); + } + + // Scope the lock + { + // Lock the thread stack while we access it + Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); + // Add this stop packet to the stop packet stack + // This stack will get popped and examined when we switch to the + // Stopped state + m_stop_packet_stack.push_back(response); } - m_last_stop_packet = response; } @@ -2256,7 +2859,18 @@ ProcessGDBRemote::IsAlive () addr_t ProcessGDBRemote::GetImageInfoAddress() { - return m_gdb_comm.GetShlibInfoAddr(); + // request the link map address via the $qShlibInfoAddr packet + lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); + + // the loaded module list can also provides a link map address + if (addr == LLDB_INVALID_ADDRESS) + { + GDBLoadedModuleInfoList list; + if (GetLoadedModuleList (list).Success()) + addr = list.m_link_map; + } + + return addr; } //------------------------------------------------------------------ @@ -2366,7 +2980,7 @@ ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Erro lldb::addr_t ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) { - lldb_private::Log *log (lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); @@ -2478,7 +3092,7 @@ ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, NULL); } - else if (!m_stdio_disable) + else if (m_stdin_forward) { m_gdb_comm.SendStdinNotification(src, src_len); } @@ -2945,28 +3559,19 @@ ProcessGDBRemote::KillDebugserverProcess () void ProcessGDBRemote::Initialize() { - static bool g_initialized = false; + static std::once_flag g_once_flag; - if (g_initialized == false) + std::call_once(g_once_flag, []() { - g_initialized = true; PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); - - Log::Callbacks log_callbacks = { - ProcessGDBRemoteLog::DisableLog, - ProcessGDBRemoteLog::EnableLog, - ProcessGDBRemoteLog::ListLogCategories - }; - - Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks); - } + }); } void -ProcessGDBRemote::DebuggerInitialize (lldb_private::Debugger &debugger) +ProcessGDBRemote::DebuggerInitialize (Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) { @@ -3018,11 +3623,40 @@ ProcessGDBRemote::StopAsyncThread () // Stop the stdio thread m_async_thread.Join(nullptr); + m_async_thread.Reset(); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } +bool +ProcessGDBRemote::HandleNotifyPacket (StringExtractorGDBRemote &packet) +{ + // get the packet at a string + const std::string &pkt = packet.GetStringRef(); + // skip %stop: + StringExtractorGDBRemote stop_info(pkt.c_str() + 5); + + // pass as a thread stop info packet + SetLastStopPacket(stop_info); + + // check for more stop reasons + HandleStopReplySequence(); + + // if the process is stopped then we need to fake a resume + // so that we can stop properly with the new break. This + // is possible due to SetPrivateState() broadcasting the + // state change as a side effect. + if (GetPrivateState() == lldb::StateType::eStateStopped) + { + SetPrivateState(lldb::StateType::eStateRunning); + } + + // since we have some stopped packets we can halt the process + SetPrivateState(lldb::StateType::eStateStopped); + + return true; +} thread_result_t ProcessGDBRemote::AsyncThread (void *arg) @@ -3040,8 +3674,9 @@ ProcessGDBRemote::AsyncThread (void *arg) if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) { - listener.StartListeningForEvents (&process->m_gdb_comm, Communication::eBroadcastBitReadThreadDidExit); - + listener.StartListeningForEvents (&process->m_gdb_comm, Communication::eBroadcastBitReadThreadDidExit | + GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify); + bool done = false; while (!done) { @@ -3071,61 +3706,77 @@ ProcessGDBRemote::AsyncThread (void *arg) if (::strstr (continue_cstr, "vAttach") == NULL) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; - StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); - - // We need to immediately clear the thread ID list so we are sure to get a valid list of threads. - // The thread ID list might be contained within the "response", or the stop reply packet that - // caused the stop. So clear it now before we give the stop reply packet to the process - // using the process->SetLastStopPacket()... - process->ClearThreadIDList (); - - switch (stop_state) + + // If in Non-Stop-Mode + if (process->GetTarget().GetNonStopModeEnabled()) { - case eStateStopped: - case eStateCrashed: - case eStateSuspended: - process->SetLastStopPacket (response); - process->SetPrivateState (stop_state); - break; - - case eStateExited: + // send the vCont packet + if (!process->GetGDBRemote().SendvContPacket(process, continue_cstr, continue_cstr_len, response)) + { + // Something went wrong + done = true; + break; + } + } + // If in All-Stop-Mode + else { - process->SetLastStopPacket (response); - process->ClearThreadIDList(); - response.SetFilePos(1); - - int exit_status = response.GetHexU8(); - const char *desc_cstr = NULL; - StringExtractor extractor; - std::string desc_string; - if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') + StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); + + // We need to immediately clear the thread ID list so we are sure to get a valid list of threads. + // The thread ID list might be contained within the "response", or the stop reply packet that + // caused the stop. So clear it now before we give the stop reply packet to the process + // using the process->SetLastStopPacket()... + process->ClearThreadIDList (); + + switch (stop_state) { - std::string desc_token; - while (response.GetNameColonValue (desc_token, desc_string)) + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + process->SetLastStopPacket (response); + process->SetPrivateState (stop_state); + break; + + case eStateExited: + { + process->SetLastStopPacket (response); + process->ClearThreadIDList(); + response.SetFilePos(1); + + int exit_status = response.GetHexU8(); + const char *desc_cstr = NULL; + StringExtractor extractor; + std::string desc_string; + if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { - if (desc_token == "description") + std::string desc_token; + while (response.GetNameColonValue (desc_token, desc_string)) { - extractor.GetStringRef().swap(desc_string); - extractor.SetFilePos(0); - extractor.GetHexByteString (desc_string); - desc_cstr = desc_string.c_str(); + if (desc_token == "description") + { + extractor.GetStringRef().swap(desc_string); + extractor.SetFilePos(0); + extractor.GetHexByteString (desc_string); + desc_cstr = desc_string.c_str(); + } } } + process->SetExitStatus(exit_status, desc_cstr); + done = true; + break; } - process->SetExitStatus(exit_status, desc_cstr); - done = true; - break; - } - case eStateInvalid: - process->SetExitStatus(-1, "lost connection"); - break; - - default: - process->SetPrivateState (stop_state); - break; - } - } - } + case eStateInvalid: + process->SetExitStatus(-1, "lost connection"); + break; + + default: + process->SetPrivateState (stop_state); + break; + } // switch(stop_state) + } // else // if in All-stop-mode + } // if (continue_packet) + } // case eBroadcastBitAysncContinue break; case eBroadcastBitAsyncThreadShouldExit: @@ -3143,10 +3794,28 @@ ProcessGDBRemote::AsyncThread (void *arg) } else if (event_sp->BroadcasterIs (&process->m_gdb_comm)) { - if (event_type & Communication::eBroadcastBitReadThreadDidExit) + switch (event_type) { - process->SetExitStatus (-1, "lost connection"); - done = true; + case Communication::eBroadcastBitReadThreadDidExit: + process->SetExitStatus (-1, "lost connection"); + done = true; + break; + + case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: + { + lldb_private::Event *event = event_sp.get(); + const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event); + StringExtractorGDBRemote notify((const char*)continue_packet->GetBytes()); + // Hand this over to the process to handle + process->HandleNotifyPacket(notify); + break; + } + + default: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); + done = true; + break; } } } @@ -3162,7 +3831,6 @@ ProcessGDBRemote::AsyncThread (void *arg) if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); - process->m_async_thread.Reset(); return NULL; } @@ -3185,13 +3853,13 @@ ProcessGDBRemote::AsyncThread (void *arg) // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, - lldb_private::StoppointCallbackContext *context, + StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // I don't think I have to do anything here, just make sure I notice the new thread when it starts to // run so I can stop it if that's what I want to do. - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log) log->Printf("Hit New Thread Notification breakpoint."); return false; @@ -3201,7 +3869,7 @@ ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, bool ProcessGDBRemote::StartNoticingNewThreads() { - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) @@ -3233,7 +3901,7 @@ ProcessGDBRemote::StartNoticingNewThreads() bool ProcessGDBRemote::StopNoticingNewThreads() { - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf ("Disabling new thread notification breakpoint."); @@ -3243,7 +3911,7 @@ ProcessGDBRemote::StopNoticingNewThreads() return true; } -lldb_private::DynamicLoader * +DynamicLoader * ProcessGDBRemote::GetDynamicLoader () { if (m_dyld_ap.get() == NULL) @@ -3392,6 +4060,659 @@ ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize (uint64_t user_specified } } +bool +ProcessGDBRemote::GetModuleSpec(const FileSpec& module_file_spec, + const ArchSpec& arch, + ModuleSpec &module_spec) +{ + Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PLATFORM); + + if (!m_gdb_comm.GetModuleInfo (module_file_spec, arch, module_spec)) + { + if (log) + log->Printf ("ProcessGDBRemote::%s - failed to get module info for %s:%s", + __FUNCTION__, module_file_spec.GetPath ().c_str (), + arch.GetTriple ().getTriple ().c_str ()); + return false; + } + + if (log) + { + StreamString stream; + module_spec.Dump (stream); + log->Printf ("ProcessGDBRemote::%s - got module info for (%s:%s) : %s", + __FUNCTION__, module_file_spec.GetPath ().c_str (), + arch.GetTriple ().getTriple ().c_str (), stream.GetString ().c_str ()); + } + + return true; +} + +namespace { + +typedef std::vector<std::string> stringVec; + +typedef std::vector<struct GdbServerRegisterInfo> GDBServerRegisterVec; +struct RegisterSetInfo +{ + ConstString name; +}; + +typedef std::map<uint32_t, RegisterSetInfo> RegisterSetMap; + +struct GdbServerTargetInfo +{ + std::string arch; + std::string osabi; + stringVec includes; + RegisterSetMap reg_set_map; + XMLNode feature_node; +}; + +bool +ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info) +{ + if (!feature_node) + return false; + + uint32_t prev_reg_num = 0; + uint32_t reg_offset = 0; + + feature_node.ForEachChildElementWithName("reg", [&target_info, &dyn_reg_info, &prev_reg_num, ®_offset](const XMLNode ®_node) -> bool { + std::string gdb_group; + std::string gdb_type; + ConstString reg_name; + ConstString alt_name; + ConstString set_name; + std::vector<uint32_t> value_regs; + std::vector<uint32_t> invalidate_regs; + bool encoding_set = false; + bool format_set = false; + RegisterInfo reg_info = { NULL, // Name + NULL, // Alt name + 0, // byte size + reg_offset, // offset + eEncodingUint, // encoding + eFormatHex, // formate + { + LLDB_INVALID_REGNUM, // GCC reg num + LLDB_INVALID_REGNUM, // DWARF reg num + LLDB_INVALID_REGNUM, // generic reg num + prev_reg_num, // GDB reg num + prev_reg_num // native register number + }, + NULL, + NULL + }; + + reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, &prev_reg_num, ®_offset](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { + if (name == "name") + { + reg_name.SetString(value); + } + else if (name == "bitsize") + { + reg_info.byte_size = StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; + } + else if (name == "type") + { + gdb_type = value.str(); + } + else if (name == "group") + { + gdb_group = value.str(); + } + else if (name == "regnum") + { + const uint32_t regnum = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); + if (regnum != LLDB_INVALID_REGNUM) + { + reg_info.kinds[eRegisterKindGDB] = regnum; + reg_info.kinds[eRegisterKindLLDB] = regnum; + prev_reg_num = regnum; + } + } + else if (name == "offset") + { + reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); + } + else if (name == "altname") + { + alt_name.SetString(value); + } + else if (name == "encoding") + { + encoding_set = true; + reg_info.encoding = Args::StringToEncoding (value.data(), eEncodingUint); + } + else if (name == "format") + { + format_set = true; + Format format = eFormatInvalid; + if (Args::StringToFormat (value.data(), format, NULL).Success()) + reg_info.format = format; + else if (value == "vector-sint8") + reg_info.format = eFormatVectorOfSInt8; + else if (value == "vector-uint8") + reg_info.format = eFormatVectorOfUInt8; + else if (value == "vector-sint16") + reg_info.format = eFormatVectorOfSInt16; + else if (value == "vector-uint16") + reg_info.format = eFormatVectorOfUInt16; + else if (value == "vector-sint32") + reg_info.format = eFormatVectorOfSInt32; + else if (value == "vector-uint32") + reg_info.format = eFormatVectorOfUInt32; + else if (value == "vector-float32") + reg_info.format = eFormatVectorOfFloat32; + else if (value == "vector-uint128") + reg_info.format = eFormatVectorOfUInt128; + } + else if (name == "group_id") + { + const uint32_t set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); + RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); + if (pos != target_info.reg_set_map.end()) + set_name = pos->second.name; + } + else if (name == "gcc_regnum") + { + reg_info.kinds[eRegisterKindGCC] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); + } + else if (name == "dwarf_regnum") + { + reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); + } + else if (name == "generic") + { + reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value.data()); + } + else if (name == "value_regnums") + { + SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); + } + else if (name == "invalidate_regnums") + { + SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); + } + else + { + printf("unhandled attribute %s = %s\n", name.data(), value.data()); + } + return true; // Keep iterating through all attributes + }); + + if (!gdb_type.empty() && !(encoding_set || format_set)) + { + if (gdb_type.find("int") == 0) + { + reg_info.format = eFormatHex; + reg_info.encoding = eEncodingUint; + } + else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") + { + reg_info.format = eFormatAddressInfo; + reg_info.encoding = eEncodingUint; + } + else if (gdb_type == "i387_ext" || gdb_type == "float") + { + reg_info.format = eFormatFloat; + reg_info.encoding = eEncodingIEEE754; + } + } + + // Only update the register set name if we didn't get a "reg_set" attribute. + // "set_name" will be empty if we didn't have a "reg_set" attribute. + if (!set_name && !gdb_group.empty()) + set_name.SetCString(gdb_group.c_str()); + + reg_info.byte_offset = reg_offset; + assert (reg_info.byte_size != 0); + reg_offset += reg_info.byte_size; + if (!value_regs.empty()) + { + value_regs.push_back(LLDB_INVALID_REGNUM); + reg_info.value_regs = value_regs.data(); + } + if (!invalidate_regs.empty()) + { + invalidate_regs.push_back(LLDB_INVALID_REGNUM); + reg_info.invalidate_regs = invalidate_regs.data(); + } + + ++prev_reg_num; + dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); + + return true; // Keep iterating through all "reg" elements + }); + return true; +} + +} // namespace {} + + +// query the target of gdb-remote for extended target information +// return: 'true' on success +// 'false' on failure +bool +ProcessGDBRemote::GetGDBServerRegisterInfo () +{ + // Make sure LLDB has an XML parser it can use first + if (!XMLDocument::XMLEnabled()) + return false; + + // redirect libxml2's error handler since the default prints to stdout + + GDBRemoteCommunicationClient & comm = m_gdb_comm; + + // check that we have extended feature read support + if ( !comm.GetQXferFeaturesReadSupported( ) ) + return false; + + // request the target xml file + std::string raw; + lldb_private::Error lldberr; + if (!comm.ReadExtFeature(ConstString("features"), + ConstString("target.xml"), + raw, + lldberr)) + { + return false; + } + + + XMLDocument xml_document; + + if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) + { + GdbServerTargetInfo target_info; + + XMLNode target_node = xml_document.GetRootElement("target"); + if (target_node) + { + XMLNode feature_node; + target_node.ForEachChildElement([&target_info, this, &feature_node](const XMLNode &node) -> bool + { + llvm::StringRef name = node.GetName(); + if (name == "architecture") + { + node.GetElementText(target_info.arch); + } + else if (name == "osabi") + { + node.GetElementText(target_info.osabi); + } + else if (name == "xi:include" || name == "include") + { + llvm::StringRef href = node.GetAttributeValue("href"); + if (!href.empty()) + target_info.includes.push_back(href.str()); + } + else if (name == "feature") + { + feature_node = node; + } + else if (name == "groups") + { + node.ForEachChildElementWithName("group", [&target_info](const XMLNode &node) -> bool { + uint32_t set_id = UINT32_MAX; + RegisterSetInfo set_info; + + node.ForEachAttribute([&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { + if (name == "id") + set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); + if (name == "name") + set_info.name = ConstString(value); + return true; // Keep iterating through all attributes + }); + + if (set_id != UINT32_MAX) + target_info.reg_set_map[set_id] = set_info; + return true; // Keep iterating through all "group" elements + }); + } + return true; // Keep iterating through all children of the target_node + }); + + if (feature_node) + { + ParseRegisters(feature_node, target_info, this->m_register_info); + } + + for (const auto &include : target_info.includes) + { + // request register file + std::string xml_data; + if (!comm.ReadExtFeature(ConstString("features"), + ConstString(include), + xml_data, + lldberr)) + continue; + + XMLDocument include_xml_document; + include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), include.c_str()); + XMLNode include_feature_node = include_xml_document.GetRootElement("feature"); + if (include_feature_node) + { + ParseRegisters(include_feature_node, target_info, this->m_register_info); + } + } + this->m_register_info.Finalize(GetTarget().GetArchitecture()); + } + } + + return m_register_info.GetNumRegisters() > 0; +} + +Error +ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) +{ + // Make sure LLDB has an XML parser it can use first + if (!XMLDocument::XMLEnabled()) + return Error (0, ErrorType::eErrorTypeGeneric); + + Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("ProcessGDBRemote::%s", __FUNCTION__); + + GDBRemoteCommunicationClient & comm = m_gdb_comm; + + // check that we have extended feature read support + if (!comm.GetQXferLibrariesSVR4ReadSupported ()) + return Error (0, ErrorType::eErrorTypeGeneric); + + list.clear (); + + // request the loaded library list + std::string raw; + lldb_private::Error lldberr; + if (!comm.ReadExtFeature (ConstString ("libraries-svr4"), ConstString (""), raw, lldberr)) + return Error (0, ErrorType::eErrorTypeGeneric); + + // parse the xml file in memory + if (log) + log->Printf ("parsing: %s", raw.c_str()); + XMLDocument doc; + + if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) + return Error (0, ErrorType::eErrorTypeGeneric); + + XMLNode root_element = doc.GetRootElement("library-list-svr4"); + if (!root_element) + return Error(); + + // main link map structure + llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); + if (!main_lm.empty()) + { + list.m_link_map = StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); + } + + root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool { + + GDBLoadedModuleInfoList::LoadedModuleInfo module; + + library.ForEachAttribute([log, &module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { + + if (name == "name") + module.set_name (value.str()); + else if (name == "lm") + { + // the address of the link_map struct. + module.set_link_map(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); + } + else if (name == "l_addr") + { + // the displacement as read from the field 'l_addr' of the link_map struct. + module.set_base(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); + + } + else if (name == "l_ld") + { + // the memory address of the libraries PT_DYAMIC section. + module.set_dynamic(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); + } + + return true; // Keep iterating over all properties of "library" + }); + + if (log) + { + std::string name; + lldb::addr_t lm=0, base=0, ld=0; + + module.get_name (name); + module.get_link_map (lm); + module.get_base (base); + module.get_dynamic (ld); + + log->Printf ("found (link_map:0x08%" PRIx64 ", base:0x08%" PRIx64 ", ld:0x08%" PRIx64 ", name:'%s')", lm, base, ld, name.c_str()); + } + + list.add (module); + return true; // Keep iterating over all "library" elements in the root node + }); + + if (log) + log->Printf ("found %" PRId32 " modules in total", (int) list.m_list.size()); + + return Error(); +} + +lldb::ModuleSP +ProcessGDBRemote::LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + bool changed = false; + + ModuleSpec module_spec (file, target.GetArchitecture()); + if ((module_sp = modules.FindFirstModule (module_spec))) + { + module_sp->SetLoadAddress (target, base_addr, true, changed); + } + else if ((module_sp = target.GetSharedModule (module_spec))) + { + module_sp->SetLoadAddress (target, base_addr, true, changed); + } + + return module_sp; +} + +size_t +ProcessGDBRemote::LoadModules () +{ + using lldb_private::process_gdb_remote::ProcessGDBRemote; + + // request a list of loaded libraries from GDBServer + GDBLoadedModuleInfoList module_list; + if (GetLoadedModuleList (module_list).Fail()) + return 0; + + // get a list of all the modules + ModuleList new_modules; + + for (GDBLoadedModuleInfoList::LoadedModuleInfo & modInfo : module_list.m_list) + { + std::string mod_name; + lldb::addr_t mod_base; + + bool valid = true; + valid &= modInfo.get_name (mod_name); + valid &= modInfo.get_base (mod_base); + if (!valid) + continue; + + // hack (cleaner way to get file name only?) (win/unix compat?) + size_t marker = mod_name.rfind ('/'); + if (marker == std::string::npos) + marker = 0; + else + marker += 1; + + FileSpec file (mod_name.c_str()+marker, true); + lldb::ModuleSP module_sp = LoadModuleAtAddress (file, mod_base); + + if (module_sp.get()) + new_modules.Append (module_sp); + } + + if (new_modules.GetSize() > 0) + { + Target & target = m_target; + + new_modules.ForEach ([&target](const lldb::ModuleSP module_sp) -> bool + { + lldb_private::ObjectFile * obj = module_sp->GetObjectFile (); + if (!obj) + return true; + + if (obj->GetType () != ObjectFile::Type::eTypeExecutable) + return true; + + lldb::ModuleSP module_copy_sp = module_sp; + target.SetExecutableModule (module_copy_sp, false); + return false; + }); + + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + loaded_modules.AppendIfNeeded (new_modules); + m_process->GetTarget().ModulesDidLoad (new_modules); + } + + return new_modules.GetSize(); +} + +Error +ProcessGDBRemote::GetFileLoadAddress(const FileSpec& file, bool& is_loaded, lldb::addr_t& load_addr) +{ + is_loaded = false; + load_addr = LLDB_INVALID_ADDRESS; + + std::string file_path = file.GetPath(false); + if (file_path.empty ()) + return Error("Empty file name specified"); + + StreamString packet; + packet.PutCString("qFileLoadAddress:"); + packet.PutCStringAsRawHex8(file_path.c_str()); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), response, false) != GDBRemoteCommunication::PacketResult::Success) + return Error("Sending qFileLoadAddress packet failed"); + + if (response.IsErrorResponse()) + { + if (response.GetError() == 1) + { + // The file is not loaded into the inferior + is_loaded = false; + load_addr = LLDB_INVALID_ADDRESS; + return Error(); + } + + return Error("Fetching file load address from remote server returned an error"); + } + + if (response.IsNormalResponse()) + { + is_loaded = true; + load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + return Error(); + } + + return Error("Unknown error happened during sending the load address packet"); +} + + +void +ProcessGDBRemote::ModulesDidLoad (ModuleList &module_list) +{ + // We must call the lldb_private::Process::ModulesDidLoad () first before we do anything + Process::ModulesDidLoad (module_list); + + // After loading shared libraries, we can ask our remote GDB server if + // it needs any symbols. + m_gdb_comm.ServeSymbolLookups(this); +} + + +class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed +{ +public: + CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process plugin packet speed-test", + "Tests packet speeds of various sizes to determine the performance characteristics of the GDB remote connection. ", + NULL), + m_option_group (interpreter), + m_num_packets (LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, "The number of packets to send of each varying size (default is 1000).", 1000), + m_max_send (LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, "The maximum number of bytes to send in a packet. Sizes increase in powers of 2 while the size is less than or equal to this option value. (default 1024).", 1024), + m_max_recv (LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, "The maximum number of bytes to receive in a packet. Sizes increase in powers of 2 while the size is less than or equal to this option value. (default 1024).", 1024), + m_json (LLDB_OPT_SET_1, false, "json", 'j', "Print the output as JSON data for easy parsing.", false, true) + { + m_option_group.Append (&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + ~CommandObjectProcessGDBRemoteSpeedTest () + { + } + + + Options * + GetOptions () override + { + return &m_option_group; + } + + bool + DoExecute (Args& command, CommandReturnObject &result) override + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + StreamSP output_stream_sp (m_interpreter.GetDebugger().GetAsyncOutputStream()); + result.SetImmediateOutputStream (output_stream_sp); + + const uint32_t num_packets = (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); + const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); + const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); + const bool json = m_json.GetOptionValue().GetCurrentValue(); + if (output_stream_sp) + process->GetGDBRemote().TestPacketSpeed (num_packets, max_send, max_recv, json, *output_stream_sp); + else + { + process->GetGDBRemote().TestPacketSpeed (num_packets, max_send, max_recv, json, result.GetOutputStream()); + } + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); + } + result.SetStatus (eReturnStatusFailed); + return false; + } +protected: + OptionGroupOptions m_option_group; + OptionGroupUInt64 m_num_packets; + OptionGroupUInt64 m_max_send; + OptionGroupUInt64 m_max_recv; + OptionGroupBoolean m_json; + +}; + class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: @@ -3410,7 +4731,7 @@ public: } bool - DoExecute (Args& command, CommandReturnObject &result) + DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) @@ -3450,7 +4771,7 @@ public: } bool - DoExecute (Args& command, CommandReturnObject &result) + DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) @@ -3498,7 +4819,7 @@ public: } bool - DoExecute (Args& command, CommandReturnObject &result) + DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) @@ -3556,7 +4877,7 @@ public: } bool - DoExecute (const char *command, CommandReturnObject &result) + DoExecute (const char *command, CommandReturnObject &result) override { if (command == NULL || command[0] == '\0') { @@ -3605,6 +4926,7 @@ public: LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessGDBRemotePacketSend (interpreter))); LoadSubCommand ("monitor", CommandObjectSP (new CommandObjectProcessGDBRemotePacketMonitor (interpreter))); LoadSubCommand ("xfer-size", CommandObjectSP (new CommandObjectProcessGDBRemotePacketXferSize (interpreter))); + LoadSubCommand ("speed-test", CommandObjectSP (new CommandObjectProcessGDBRemoteSpeedTest (interpreter))); } ~CommandObjectProcessGDBRemotePacket () diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index e0c460a202d6..0a5e4a8a41b4 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -34,29 +34,32 @@ #include "Utility/StringExtractor.h" #include "GDBRemoteRegisterContext.h" +namespace lldb_private { +namespace process_gdb_remote { + class ThreadGDBRemote; -class ProcessGDBRemote : public lldb_private::Process +class ProcessGDBRemote : public Process { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ static lldb::ProcessSP - CreateInstance (lldb_private::Target& target, - lldb_private::Listener &listener, - const lldb_private::FileSpec *crash_file_path); + CreateInstance (Target& target, + Listener &listener, + const FileSpec *crash_file_path); static void Initialize(); static void - DebuggerInitialize (lldb_private::Debugger &debugger); + DebuggerInitialize (Debugger &debugger); static void Terminate(); - static lldb_private::ConstString + static ConstString GetPluginNameStatic(); static const char * @@ -65,7 +68,7 @@ public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ - ProcessGDBRemote(lldb_private::Target& target, lldb_private::Listener &listener); + ProcessGDBRemote(Target& target, Listener &listener); virtual ~ProcessGDBRemote(); @@ -73,149 +76,143 @@ public: //------------------------------------------------------------------ // Check if a given Process //------------------------------------------------------------------ - virtual bool - CanDebug (lldb_private::Target &target, - bool plugin_specified_by_name) override; + bool + CanDebug (Target &target, bool plugin_specified_by_name) override; - virtual lldb_private::CommandObject * + CommandObject * GetPluginCommandObject() override; //------------------------------------------------------------------ // Creating a new process, or attaching to an existing one //------------------------------------------------------------------ - virtual lldb_private::Error - WillLaunch (lldb_private::Module* module) override; + Error + WillLaunch (Module* module) override; - virtual lldb_private::Error - DoLaunch (lldb_private::Module *exe_module, - lldb_private::ProcessLaunchInfo &launch_info) override; + Error + DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) override; - virtual void + void DidLaunch () override; - virtual lldb_private::Error + Error WillAttachToProcessWithID (lldb::pid_t pid) override; - virtual lldb_private::Error + Error WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) override; - virtual lldb_private::Error - DoConnectRemote (lldb_private::Stream *strm, const char *remote_url) override; + Error + DoConnectRemote (Stream *strm, const char *remote_url) override; - lldb_private::Error + Error WillLaunchOrAttach (); - - virtual lldb_private::Error - DoAttachToProcessWithID (lldb::pid_t pid) override; - virtual lldb_private::Error - DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; + Error + DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) override; - virtual lldb_private::Error + Error DoAttachToProcessWithName (const char *process_name, - const lldb_private::ProcessAttachInfo &attach_info) override; + const ProcessAttachInfo &attach_info) override; - virtual void - DidAttach (lldb_private::ArchSpec &process_arch) override; + void + DidAttach (ArchSpec &process_arch) override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ - virtual lldb_private::ConstString + ConstString GetPluginName() override; - virtual uint32_t + uint32_t GetPluginVersion() override; //------------------------------------------------------------------ // Process Control //------------------------------------------------------------------ - virtual lldb_private::Error + Error WillResume () override; - virtual lldb_private::Error + Error DoResume () override; - virtual lldb_private::Error + Error DoHalt (bool &caused_stop) override; - virtual lldb_private::Error + Error DoDetach (bool keep_stopped) override; - virtual bool + bool DetachRequiresHalt() override { return true; } - virtual lldb_private::Error + Error DoSignal (int signal) override; - virtual lldb_private::Error + Error DoDestroy () override; - virtual void + void RefreshStateAfterStop() override; //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ - virtual bool + bool IsAlive () override; - virtual lldb::addr_t + lldb::addr_t GetImageInfoAddress() override; //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ - virtual size_t - DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; + size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &error) override; - virtual size_t - DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error) override; + size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error &error) override; - virtual lldb::addr_t - DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error) override; + lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, Error &error) override; - virtual lldb_private::Error - GetMemoryRegionInfo (lldb::addr_t load_addr, - lldb_private::MemoryRegionInfo ®ion_info) override; + Error + GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; - virtual lldb_private::Error + Error DoDeallocateMemory (lldb::addr_t ptr) override; //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ - virtual size_t - PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error) override; + size_t + PutSTDIN (const char *buf, size_t buf_size, Error &error) override; //---------------------------------------------------------------------- // Process Breakpoints //---------------------------------------------------------------------- - virtual lldb_private::Error - EnableBreakpointSite (lldb_private::BreakpointSite *bp_site) override; + Error + EnableBreakpointSite (BreakpointSite *bp_site) override; - virtual lldb_private::Error - DisableBreakpointSite (lldb_private::BreakpointSite *bp_site) override; + Error + DisableBreakpointSite (BreakpointSite *bp_site) override; //---------------------------------------------------------------------- // Process Watchpoints //---------------------------------------------------------------------- - virtual lldb_private::Error - EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true) override; + Error + EnableWatchpoint (Watchpoint *wp, bool notify = true) override; - virtual lldb_private::Error - DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true) override; + Error + DisableWatchpoint (Watchpoint *wp, bool notify = true) override; - virtual lldb_private::Error + Error GetWatchpointSupportInfo (uint32_t &num) override; - virtual lldb_private::Error + Error GetWatchpointSupportInfo (uint32_t &num, bool& after) override; - virtual bool + bool StartNoticingNewThreads() override; - virtual bool + bool StopNoticingNewThreads() override; GDBRemoteCommunicationClient & @@ -224,23 +221,39 @@ public: return m_gdb_comm; } - virtual lldb_private::Error + Error SendEventData(const char *data) override; //---------------------------------------------------------------------- - // Override SetExitStatus so we can disconnect from the remote GDB server + // Override DidExit so we can disconnect from the remote GDB server //---------------------------------------------------------------------- - virtual bool - SetExitStatus (int exit_status, const char *cstr) override; + void + DidExit () override; void SetUserSpecifiedMaxMemoryTransferSize (uint64_t user_specified_max); + bool + GetModuleSpec(const FileSpec& module_file_spec, + const ArchSpec& arch, + ModuleSpec &module_spec) override; + + size_t + LoadModules() override; + + Error + GetFileLoadAddress(const FileSpec& file, bool& is_loaded, lldb::addr_t& load_addr) override; + + void + ModulesDidLoad (ModuleList &module_list) override; + protected: friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; friend class GDBRemoteRegisterContext; + class GDBLoadedModuleInfoList; + //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- @@ -273,24 +286,24 @@ protected: void Clear ( ); - lldb_private::Flags & + Flags & GetFlags () { return m_flags; } - const lldb_private::Flags & + const Flags & GetFlags () const { return m_flags; } - virtual bool - UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list) override; + bool + UpdateThreadList (ThreadList &old_thread_list, + ThreadList &new_thread_list) override; - lldb_private::Error - LaunchAndConnectToDebugserver (const lldb_private::ProcessInfo &process_info); + Error + LaunchAndConnectToDebugserver (const ProcessInfo &process_info); void KillDebugserverProcess (); @@ -302,20 +315,23 @@ protected: SetLastStopPacket (const StringExtractorGDBRemote &response); bool - ParsePythonTargetDefinition(const lldb_private::FileSpec &target_definition_fspec); - - bool - ParseRegisters(lldb_private::ScriptInterpreterObject *registers_array); + ParsePythonTargetDefinition(const FileSpec &target_definition_fspec); const lldb::DataBufferSP GetAuxvData() override; - lldb_private::StructuredData::ObjectSP + StructuredData::ObjectSP GetExtendedInfoForThread (lldb::tid_t tid); void GetMaxMemorySize(); + bool + CalculateThreadStopInfo (ThreadGDBRemote *thread); + + size_t + UpdateThreadIDsFromStopReplyThreadsValue (std::string &value); + //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ @@ -326,19 +342,21 @@ protected: eBroadcastBitAsyncThreadDidExit = (1 << 2) }; - lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) + Flags m_flags; // Process specific flags (see eFlags enums) GDBRemoteCommunicationClient m_gdb_comm; std::atomic<lldb::pid_t> m_debugserver_pid; - StringExtractorGDBRemote m_last_stop_packet; - lldb_private::Mutex m_last_stop_packet_mutex; + std::vector<StringExtractorGDBRemote> m_stop_packet_stack; // The stop packet stack replaces the last stop packet variable + Mutex m_last_stop_packet_mutex; GDBRemoteDynamicRegisterInfo m_register_info; - lldb_private::Broadcaster m_async_broadcaster; - lldb_private::HostThread m_async_thread; - lldb_private::Mutex m_async_thread_state_mutex; + Broadcaster m_async_broadcaster; + HostThread m_async_thread; + Mutex m_async_thread_state_mutex; typedef std::vector<lldb::tid_t> tid_collection; typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection; typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; + typedef std::map<uint32_t, std::string> ExpeditedRegisterMap; tid_collection m_thread_ids; // Thread IDs for all threads. This list gets updated after stopping + StructuredData::ObjectSP m_threads_info_sp; // Stop info for all threads if "jThreadsInfo" packet is supported tid_collection m_continue_c_tids; // 'c' for continue tid_sig_collection m_continue_C_tids; // 'C' for continue with signal tid_collection m_continue_s_tids; // 's' for step @@ -351,6 +369,10 @@ protected: bool m_destroy_tried_resuming; lldb::CommandObjectSP m_command_sp; int64_t m_breakpoint_pc_offset; + lldb::tid_t m_initial_tid; // The inital thread ID, given by stub on attach + + bool + HandleNotifyPacket(StringExtractorGDBRemote &packet); bool StartAsyncThread (); @@ -371,6 +393,27 @@ protected: lldb::StateType SetThreadStopInfo (StringExtractor& stop_packet); + lldb::StateType + SetThreadStopInfo (StructuredData::Dictionary *thread_dict); + + lldb::ThreadSP + SetThreadStopInfo (lldb::tid_t tid, + ExpeditedRegisterMap &expedited_register_map, + uint8_t signo, + const std::string &thread_name, + const std::string &reason, + const std::string &description, + uint32_t exc_type, + const std::vector<lldb::addr_t> &exc_data, + lldb::addr_t thread_dispatch_qaddr, + bool queue_vars_valid, + std::string &queue_name, + lldb::QueueKind queue_kind, + uint64_t queue_serial); + + void + HandleStopReplySequence (); + void ClearThreadIDList (); @@ -378,25 +421,36 @@ protected: UpdateThreadIDList (); void - DidLaunchOrAttach (lldb_private::ArchSpec& process_arch); + DidLaunchOrAttach (ArchSpec& process_arch); - lldb_private::Error + Error ConnectToDebugserver (const char *host_port); const char * GetDispatchQueueNameForThread (lldb::addr_t thread_dispatch_qaddr, std::string &dispatch_queue_name); - lldb_private::DynamicLoader * + DynamicLoader * GetDynamicLoader () override; + // Query remote GDBServer for register information + bool + GetGDBServerRegisterInfo (); + + // Query remote GDBServer for a detailed loaded library list + Error + GetLoadedModuleList (GDBLoadedModuleInfoList &); + + lldb::ModuleSP + LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr); + private: //------------------------------------------------------------------ // For ProcessGDBRemote only //------------------------------------------------------------------ static bool NewThreadNotifyBreakpointHit (void *baton, - lldb_private::StoppointCallbackContext *context, + StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); @@ -404,4 +458,7 @@ private: }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // liblldb_ProcessGDBRemote_h_ diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp index 15b861feaa82..d4726adc890e 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp @@ -9,6 +9,8 @@ #include "ProcessGDBRemoteLog.h" +#include <mutex> + #include "lldb/Interpreter/Args.h" #include "lldb/Core/StreamFile.h" @@ -16,6 +18,7 @@ using namespace lldb; using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; // We want to avoid global constructors where code needs to be run so here we @@ -32,6 +35,22 @@ GetLog () return g_log; } +void +ProcessGDBRemoteLog::Initialize() +{ + static ConstString g_name("gdb-remote"); + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, [](){ + Log::Callbacks log_callbacks = { + DisableLog, + EnableLog, + ListLogCategories + }; + + Log::RegisterLogChannel (g_name, log_callbacks); + }); +} Log * ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (uint32_t mask) diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h index 93734067f133..3cd974d7d821 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h @@ -32,26 +32,35 @@ #define GDBR_LOG_ALL (UINT32_MAX) #define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS +namespace lldb_private { +namespace process_gdb_remote { + class ProcessGDBRemoteLog { public: - static lldb_private::Log * + static void + Initialize(); + + static Log * GetLogIfAllCategoriesSet(uint32_t mask = 0); - static lldb_private::Log * + static Log * GetLogIfAnyCategoryIsSet (uint32_t mask); static void - DisableLog (const char **categories, lldb_private::Stream *feedback_strm); + DisableLog (const char **categories, Stream *feedback_strm); - static lldb_private::Log * - EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, lldb_private::Stream *feedback_strm); + static Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm); static void - ListLogCategories (lldb_private::Stream *strm); + ListLogCategories (Stream *strm); static void LogIf (uint32_t mask, const char *format, ...); }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // liblldb_ProcessGDBRemoteLog_h_ diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp index 5c70cb5427cb..59c701d75a68 100644 --- a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -21,6 +21,7 @@ #include "lldb/Target/StopInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" #include "lldb/Target/Unwind.h" #include "ProcessGDBRemote.h" @@ -29,6 +30,7 @@ using namespace lldb; using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // Thread Registers @@ -38,9 +40,11 @@ ThreadGDBRemote::ThreadGDBRemote (Process &process, lldb::tid_t tid) : Thread(process, tid), m_thread_name (), m_dispatch_queue_name (), - m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS) + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS), + m_queue_kind(eQueueKindUnknown), + m_queue_serial(0) { - ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, process.GetID(), GetID()); @@ -64,10 +68,36 @@ ThreadGDBRemote::GetName () return m_thread_name.c_str(); } +void +ThreadGDBRemote::ClearQueueInfo () +{ + m_dispatch_queue_name.clear(); + m_queue_kind = eQueueKindUnknown; + m_queue_serial = 0; +} + +void +ThreadGDBRemote::SetQueueInfo (std::string &&queue_name, QueueKind queue_kind, uint64_t queue_serial) +{ + m_dispatch_queue_name = queue_name; + m_queue_kind = queue_kind; + m_queue_serial = queue_serial; +} + const char * ThreadGDBRemote::GetQueueName () { + // If our cached queue info is valid, then someone called ThreadGDBRemote::SetQueueInfo(...) + // with valid information that was gleaned from the stop reply packet. In this case we trust + // that the info is valid in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) + { + if (m_dispatch_queue_name.empty()) + return nullptr; + else + return m_dispatch_queue_name.c_str(); + } // Always re-fetch the dispatch queue name since it can change if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) @@ -77,13 +107,12 @@ ThreadGDBRemote::GetQueueName () { SystemRuntime *runtime = process_sp->GetSystemRuntime (); if (runtime) - { m_dispatch_queue_name = runtime->GetQueueNameFromThreadQAddress (m_thread_dispatch_qaddr); - } - if (m_dispatch_queue_name.length() > 0) - { + else + m_dispatch_queue_name.clear(); + + if (!m_dispatch_queue_name.empty()) return m_dispatch_queue_name.c_str(); - } } } return NULL; @@ -92,6 +121,12 @@ ThreadGDBRemote::GetQueueName () queue_id_t ThreadGDBRemote::GetQueueID () { + // If our cached queue info is valid, then someone called ThreadGDBRemote::SetQueueInfo(...) + // with valid information that was gleaned from the stop reply packet. In this case we trust + // that the info is valid in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) + return m_queue_serial; + if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { ProcessSP process_sp (GetProcess()); @@ -147,7 +182,7 @@ ThreadGDBRemote::FetchThreadExtendedInfo () { StructuredData::ObjectSP object_sp; const lldb::user_id_t tid = GetProtocolID(); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD)); + Log *log(GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD)); if (log) log->Printf ("Fetching extended information for thread %4.4" PRIx64, tid); ProcessSP process_sp (GetProcess()); @@ -164,7 +199,7 @@ ThreadGDBRemote::WillResume (StateType resume_state) { int signo = GetResumeSignal(); const lldb::user_id_t tid = GetProtocolID(); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD)); + Log *log(GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD)); if (log) log->Printf ("Resuming thread: %4.4" PRIx64 " with state: %s.", tid, StateAsCString(resume_state)); @@ -282,12 +317,7 @@ ThreadGDBRemote::CalculateStopInfo () { ProcessSP process_sp (GetProcess()); if (process_sp) - { - StringExtractorGDBRemote stop_packet; - ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); - if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet)) - return gdb_process->SetThreadStopInfo (stop_packet) == eStateStopped; - } + return static_cast<ProcessGDBRemote *>(process_sp.get())->CalculateThreadStopInfo(this); return false; } diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h index a204a917ecb3..175433a3e20c 100644 --- a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -17,45 +17,49 @@ #include "lldb/Target/Thread.h" class StringExtractor; + +namespace lldb_private { +namespace process_gdb_remote { + class ProcessGDBRemote; -class ThreadGDBRemote : public lldb_private::Thread +class ThreadGDBRemote : public Thread { public: - ThreadGDBRemote (lldb_private::Process &process, lldb::tid_t tid); + ThreadGDBRemote (Process &process, lldb::tid_t tid); virtual ~ThreadGDBRemote (); - virtual void - WillResume (lldb::StateType resume_state); + void + WillResume (lldb::StateType resume_state) override; - virtual void - RefreshStateAfterStop(); + void + RefreshStateAfterStop() override; - virtual const char * - GetName (); + const char * + GetName () override; - virtual const char * - GetQueueName (); + const char * + GetQueueName () override; - virtual lldb::queue_id_t - GetQueueID (); + lldb::queue_id_t + GetQueueID () override; - virtual lldb::QueueSP - GetQueue (); + lldb::QueueSP + GetQueue () override; lldb::addr_t - GetQueueLibdispatchQueueAddress (); + GetQueueLibdispatchQueueAddress () override; - virtual lldb::RegisterContextSP - GetRegisterContext (); + lldb::RegisterContextSP + GetRegisterContext () override; - virtual lldb::RegisterContextSP - CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + lldb::RegisterContextSP + CreateRegisterContextForFrame (StackFrame *frame) override; void - Dump (lldb_private::Log *log, uint32_t index); + Dump (Log *log, uint32_t index); static bool ThreadIDIsValid (lldb::tid_t thread); @@ -67,7 +71,7 @@ public: GetBasicInfoAsString (); void - SetName (const char *name) + SetName (const char *name) override { if (name && name[0]) m_thread_name.assign (name); @@ -87,8 +91,14 @@ public: m_thread_dispatch_qaddr = thread_dispatch_qaddr; } - lldb_private::StructuredData::ObjectSP - FetchThreadExtendedInfo (); + void + ClearQueueInfo (); + + void + SetQueueInfo (std::string &&queue_name, lldb::QueueKind queue_kind, uint64_t queue_serial); + + StructuredData::ObjectSP + FetchThreadExtendedInfo () override; protected: @@ -97,13 +107,20 @@ protected: bool PrivateSetRegisterValue (uint32_t reg, StringExtractor &response); - + + bool + CachedQueueInfoIsValid() const + { + return m_queue_kind != lldb::eQueueKindUnknown; + } //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ std::string m_thread_name; std::string m_dispatch_queue_name; lldb::addr_t m_thread_dispatch_qaddr; + lldb::QueueKind m_queue_kind; // Queue info from stop reply/stop info for thread + uint64_t m_queue_serial; // Queue info from stop reply/stop info for thread //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ @@ -111,10 +128,11 @@ protected: void SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id); - virtual bool - CalculateStopInfo (); - - + bool + CalculateStopInfo () override; }; +} // namespace process_gdb_remote +} // namespace lldb_private + #endif // liblldb_ThreadGDBRemote_h_ |