diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp')
| -rw-r--r-- | source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 2292 | 
1 files changed, 1807 insertions, 485 deletions
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 ()  | 
