diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp')
| -rw-r--r-- | source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp | 623 | 
1 files changed, 551 insertions, 72 deletions
| diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 3a14e9fe759a5..df036cdcc8e00 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -7,8 +7,10 @@  //  //===----------------------------------------------------------------------===// +#include <errno.h>  #include "GDBRemoteCommunicationServer.h" +#include "lldb/Core/StreamGDBRemote.h"  // C Includes  // C++ Includes @@ -20,6 +22,7 @@  #include "lldb/Core/State.h"  #include "lldb/Core/StreamString.h"  #include "lldb/Host/Endian.h" +#include "lldb/Host/File.h"  #include "lldb/Host/Host.h"  #include "lldb/Host/TimeValue.h"  #include "lldb/Target/Process.h" @@ -40,11 +43,31 @@ GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) :      m_async_thread (LLDB_INVALID_HOST_THREAD),      m_process_launch_info (),      m_process_launch_error (), +    m_spawned_pids (), +    m_spawned_pids_mutex (Mutex::eMutexTypeRecursive),      m_proc_infos (),      m_proc_infos_index (0),      m_lo_port_num (0), -    m_hi_port_num (0) +    m_hi_port_num (0), +    m_next_port (0), +    m_use_port_range (false)  { +    // We seldom need to override the port number that the debugserver process +    // starts with.  We just pass in 0 to let the system choose a random port. +    // In rare situation where the need arises, use two environment variables +    // to override. +    uint16_t lo_port_num = 0; +    uint16_t hi_port_num = 0; +    const char *lo_port_c_str = getenv("LLDB_PLATFORM_START_DEBUG_SERVER_LO_PORT"); +    if (lo_port_c_str) +        lo_port_num = ::atoi(lo_port_c_str); +    const char *hi_port_c_str = getenv("LLDB_PLATFORM_START_DEBUG_SERVER_HI_PORT"); +    if (hi_port_c_str) +        hi_port_num = ::atoi(hi_port_c_str); +    if (lo_port_num && hi_port_num && lo_port_num < hi_port_num) +    { +        SetPortRange(lo_port_num, hi_port_num); +    }  }  //---------------------------------------------------------------------- @@ -65,7 +88,7 @@ GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer()  //        log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID());  //  //    StringExtractorGDBRemote packet; -//     +//  //    while ()  //    {  //        if (packet. @@ -79,9 +102,9 @@ GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer()  //}  //  bool -GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,  +GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,                                                          Error &error, -                                                        bool &interrupt,  +                                                        bool &interrupt,                                                          bool &quit)  {      StringExtractorGDBRemote packet; @@ -103,7 +126,7 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,                  error.SetErrorString("interrupt received");                  interrupt = true;                  break; -             +              case StringExtractorGDBRemote::eServerPacketType_unimplemented:                  return SendUnimplementedResponse (packet.GetStringRef().c_str()) > 0; @@ -112,22 +135,25 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,              case StringExtractorGDBRemote::eServerPacketType_qfProcessInfo:                  return Handle_qfProcessInfo (packet); -                 +              case StringExtractorGDBRemote::eServerPacketType_qsProcessInfo:                  return Handle_qsProcessInfo (packet); -                 +              case StringExtractorGDBRemote::eServerPacketType_qC:                  return Handle_qC (packet); -                 +              case StringExtractorGDBRemote::eServerPacketType_qHostInfo:                  return Handle_qHostInfo (packet); -                 +              case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer:                  return Handle_qLaunchGDBServer (packet); -                 + +            case StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess: +                return Handle_qKillSpawnedProcess (packet); +              case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess:                  return Handle_qLaunchSuccess (packet); -                 +              case StringExtractorGDBRemote::eServerPacketType_qGroupName:                  return Handle_qGroupName (packet); @@ -142,24 +168,60 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,              case StringExtractorGDBRemote::eServerPacketType_QEnvironment:                  return Handle_QEnvironment (packet); -             + +            case StringExtractorGDBRemote::eServerPacketType_QLaunchArch: +                return Handle_QLaunchArch (packet); +              case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR:                  return Handle_QSetDisableASLR (packet); -             +              case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN:                  return Handle_QSetSTDIN (packet); -             +              case StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT:                  return Handle_QSetSTDOUT (packet); -             +              case StringExtractorGDBRemote::eServerPacketType_QSetSTDERR:                  return Handle_QSetSTDERR (packet); -             +              case StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir:                  return Handle_QSetWorkingDir (packet);              case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode:                  return Handle_QStartNoAckMode (packet); + +            case StringExtractorGDBRemote::eServerPacketType_qPlatform_IO_MkDir: +                return Handle_qPlatform_IO_MkDir (packet); + +            case StringExtractorGDBRemote::eServerPacketType_qPlatform_RunCommand: +                return Handle_qPlatform_RunCommand (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_Open: +                return Handle_vFile_Open (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_Close: +                return Handle_vFile_Close (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_pRead: +                return Handle_vFile_pRead (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_pWrite: +                return Handle_vFile_pWrite (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_Size: +                return Handle_vFile_Size (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_Mode: +                return Handle_vFile_Mode (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_Exists: +                return Handle_vFile_Exists (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_Stat: +                return Handle_vFile_Stat (packet); + +            case StringExtractorGDBRemote::eServerPacketType_vFile_MD5: +                return Handle_vFile_MD5 (packet);          }          return true;      } @@ -207,7 +269,7 @@ bool  GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet)  {      StreamString response; -     +      // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00      ArchSpec host_arch (Host::GetArchitecture ()); @@ -222,12 +284,12 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet          response.Printf ("cputype:%u;", cpu);      if (sub != LLDB_INVALID_CPUTYPE)          response.Printf ("cpusubtype:%u;", sub); -     +      if (cpu == ArchSpec::kCore_arm_any)          response.Printf("watchpoint_exceptions_received:before;");   // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes.      else          response.Printf("watchpoint_exceptions_received:after;"); -     +      switch (lldb::endian::InlHostByteOrder())      {      case eByteOrderBig:     response.PutCString ("endian:big;"); break; @@ -235,7 +297,7 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet      case eByteOrderPDP:     response.PutCString ("endian:pdp;"); break;      default:                response.PutCString ("endian:unknown;"); break;      } -     +      uint32_t major = UINT32_MAX;      uint32_t minor = UINT32_MAX;      uint32_t update = UINT32_MAX; @@ -273,7 +335,7 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet          response.PutCStringAsRawHex8(s.c_str());          response.PutChar(';');      } -     +      return SendPacketNoLock (response.GetData(), response.GetSize()) > 0;  } @@ -319,7 +381,7 @@ GDBRemoteCommunicationServer::Handle_qProcessInfoPID (StringExtractorGDBRemote &      return SendErrorResponse (1);  } -bool  +bool  GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &packet)  {      m_proc_infos_index = 0; @@ -329,7 +391,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa      packet.SetFilePos(::strlen ("qfProcessInfo"));      if (packet.GetChar() == ':')      { -     +          std::string key;          std::string value;          while (packet.GetNameColonValue(key, value)) @@ -360,11 +422,11 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa                  {                      match_info.SetNameMatchType (eNameMatchContains);                  } -                else if (value.compare("regex") == 0)        +                else if (value.compare("regex") == 0)                  {                      match_info.SetNameMatchType (eNameMatchRegularExpression);                  } -                else  +                else                  {                      success = false;                  } @@ -405,7 +467,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa              {                  success = false;              } -             +              if (!success)                  return SendErrorResponse (2);          } @@ -420,7 +482,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa      return SendErrorResponse (3);  } -bool  +bool  GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &packet)  {      if (m_proc_infos_index < m_proc_infos.GetSize()) @@ -433,7 +495,7 @@ GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &pa      return SendErrorResponse (4);  } -bool  +bool  GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet)  {      // Packet format: "qUserName:%i" where %i is the uid @@ -450,10 +512,10 @@ GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet          }      }      return SendErrorResponse (5); -     +  } -bool  +bool  GDBRemoteCommunicationServer::Handle_qGroupName (StringExtractorGDBRemote &packet)  {      // Packet format: "qGroupName:%i" where %i is the gid @@ -539,11 +601,11 @@ AcceptPortFromInferior (void *arg)  //    for (int i=0; i<num_retries; i++)  //    {  //        struct proc_bsdinfo bsd_info; -//        int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO,  -//                                    (uint64_t) 0,  -//                                    &bsd_info,  +//        int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, +//                                    (uint64_t) 0, +//                                    &bsd_info,  //                                    PROC_PIDTBSDINFO_SIZE); -//         +//  //        switch (error)  //        {  //            case EINVAL: @@ -551,10 +613,10 @@ AcceptPortFromInferior (void *arg)  //            case ESRCH:  //            case EPERM:  //                return false; -//                 +//  //            default:  //                break; -//                 +//  //            case 0:  //                if (bsd_info.pbi_status == SSTOP)  //                    return true; @@ -567,9 +629,9 @@ AcceptPortFromInferior (void *arg)  bool  GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet)  { -    // The 'A' packet is the most over designed packet ever here with  -    // redundant argument indexes, redundant argument lengths and needed hex  -    // encoded argument string values. Really all that is needed is a comma  +    // The 'A' packet is the most over designed packet ever here with +    // redundant argument indexes, redundant argument lengths and needed hex +    // encoded argument string values. Really all that is needed is a comma      // separated hex encoded argument value list, but we will stay true to the      // documented version of the 'A' packet here... @@ -615,7 +677,7 @@ GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet)                                  if (packet.GetChar() != ',')                                      success = false;                              } -                             +                              if (success)                              {                                  if (arg_idx == 0) @@ -649,11 +711,11 @@ GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet)      response.Printf("QC%" PRIx64, pid);      if (m_is_platform)      { -        // If we launch a process and this GDB server is acting as a platform,  -        // then we need to clear the process launch state so we can start  +        // If we launch a process and this GDB server is acting as a platform, +        // then we need to clear the process launch state so we can start          // launching another process. In order to launch a process a bunch or          // packets need to be sent: environment packets, working directory, -        // disable ASLR, and many more settings. When we launch a process we  +        // disable ASLR, and many more settings. When we launch a process we          // then need to know when to clear this information. Currently we are          // selecting the 'qC' packet as that packet which seems to make the most          // sense. @@ -666,8 +728,30 @@ GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet)  }  bool +GDBRemoteCommunicationServer::DebugserverProcessReaped (lldb::pid_t pid) +{ +    Mutex::Locker locker (m_spawned_pids_mutex); +    return m_spawned_pids.erase(pid) > 0; +} +bool +GDBRemoteCommunicationServer::ReapDebugserverProcess (void *callback_baton, +                                                      lldb::pid_t pid, +                                                      bool exited, +                                                      int signal,    // Zero for no signal +                                                      int status)    // Exit value of process if signal is zero +{ +    GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton; +    server->DebugserverProcessReaped (pid); +    return true; +} + +bool  GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet)  { +#ifdef _WIN32 +    // No unix sockets on windows +    return false; +#else      // Spawn a local debugserver as a platform so we can then attach or launch      // a process... @@ -677,49 +761,101 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote          ConnectionFileDescriptor file_conn;          char connect_url[PATH_MAX];          Error error; -        char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";     -        if (::mktemp (unix_socket_name) == NULL) +        std::string hostname; +        // TODO: /tmp/ should not be hardcoded. User might want to override /tmp +        // with the TMPDIR environnement variable +        char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; +        if (::mkstemp (unix_socket_name) == -1)          { -            error.SetErrorString ("failed to make temporary path for a unix socket"); +            error.SetErrorStringWithFormat("failed to make temporary path for a unix socket: %s", strerror(errno));          }          else          { +            packet.SetFilePos(::strlen ("qLaunchGDBServer:")); +            std::string name; +            std::string value; +            uint16_t port = UINT16_MAX; +            while (packet.GetNameColonValue(name, value)) +            { +                if (name.compare ("host") == 0) +                    hostname.swap(value); +                else if (name.compare ("port") == 0) +                    port = Args::StringToUInt32(value.c_str(), 0, 0); +            } +            if (port == UINT16_MAX) +                port = GetAndUpdateNextPort(); +              ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name);              // Spawn a new thread to accept the port that gets bound after              // binding to port 0 (zero). -            lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name, -                                                               AcceptPortFromInferior, -                                                               connect_url, -                                                               &error); -             +            lldb::thread_t accept_thread = LLDB_INVALID_HOST_THREAD; + +            if (port == 0) +            { +                accept_thread = Host::ThreadCreate (unix_socket_name, +                                                    AcceptPortFromInferior, +                                                    connect_url, +                                                    &error); +            } +              if (IS_VALID_LLDB_HOST_THREAD(accept_thread))              { -                // Spawn a debugserver and try to get +                // Spawn a debugserver and try to get the port it listens to.                  ProcessLaunchInfo debugserver_launch_info; -                error = StartDebugserverProcess ("localhost:0",  -                                                 unix_socket_name,  +                StreamString host_and_port; +                if (hostname.empty()) +                    hostname = "localhost"; +                host_and_port.Printf("%s:%u", hostname.c_str(), port); +                const char *host_and_port_cstr = host_and_port.GetString().c_str(); +                Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); +                if (log) +                    log->Printf("Launching debugserver with: %s...\n", host_and_port_cstr); +                error = StartDebugserverProcess (host_and_port_cstr, +                                                 unix_socket_name,                                                   debugserver_launch_info); -                 +                  lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); + +                if (debugserver_pid != LLDB_INVALID_PROCESS_ID) +                { +                    { +                        Mutex::Locker locker (m_spawned_pids_mutex); +                        m_spawned_pids.insert(debugserver_pid); +                    } +                    Host::StartMonitoringChildProcess (ReapDebugserverProcess, this, debugserver_pid, false); +                } +                  if (error.Success())                  {                      bool success = false; -                     -                    thread_result_t accept_thread_result = NULL; -                    if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) + +                    if (accept_thread)                      { -                        if (accept_thread_result) +                        thread_result_t accept_thread_result = NULL; +                        if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error))                          { -                            uint16_t port = (intptr_t)accept_thread_result; -                            char response[256]; -                            const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); -                            assert (response_len < (int)sizeof(response)); -                            //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); -                            success = SendPacketNoLock (response, response_len) > 0; +                            if (accept_thread_result) +                            { +                                port = (intptr_t)accept_thread_result; +                                char response[256]; +                                const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); +                                assert (response_len < sizeof(response)); +                                //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); +                                success = SendPacketNoLock (response, response_len) > 0; +                            }                          }                      } +                    else +                    { +                        char response[256]; +                        const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); +                        assert (response_len < sizeof(response)); +                        //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); +                        success = SendPacketNoLock (response, response_len) > 0; + +                    }                      ::unlink (unix_socket_name); -                     +                      if (!success)                      {                          if (debugserver_pid != LLDB_INVALID_PROCESS_ID) @@ -730,7 +866,61 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote              }          }      } -    return SendErrorResponse (13); +    return SendErrorResponse (9); +#endif +} + +bool +GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) +{ +    // Spawn a local debugserver as a platform so we can then attach or launch +    // a process... + +    if (m_is_platform) +    { +        packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); + +        lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + +        // Scope for locker +        { +            Mutex::Locker locker (m_spawned_pids_mutex); +            if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +                return SendErrorResponse (10); +        } +        Host::Kill (pid, SIGTERM); + +        for (size_t i=0; i<10; ++i) +        { +            // Scope for locker +            { +                Mutex::Locker locker (m_spawned_pids_mutex); +                if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +                    return true; +            } +            usleep (10000); +        } + +        // Scope for locker +        { +            Mutex::Locker locker (m_spawned_pids_mutex); +            if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +                return true; +        } +        Host::Kill (pid, SIGKILL); + +        for (size_t i=0; i<10; ++i) +        { +            // Scope for locker +            { +                Mutex::Locker locker (m_spawned_pids_mutex); +                if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +                    return true; +            } +            usleep (10000); +        } +    } +    return SendErrorResponse (10);  }  bool @@ -738,7 +928,7 @@ GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &p  {      if (m_process_launch_error.Success())          return SendOKResponse(); -    StreamString response;     +    StreamString response;      response.PutChar('E');      response.PutCString(m_process_launch_error.AsCString("<unknown error>"));      return SendPacketNoLock (response.GetData(), response.GetSize()); @@ -754,7 +944,22 @@ GDBRemoteCommunicationServer::Handle_QEnvironment  (StringExtractorGDBRemote &pa          m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek());          return SendOKResponse ();      } -    return SendErrorResponse (9); +    return SendErrorResponse (11); +} + +bool +GDBRemoteCommunicationServer::Handle_QLaunchArch (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen ("QLaunchArch:")); +    const uint32_t bytes_left = packet.GetBytesLeft(); +    if (bytes_left > 0) +    { +        const char* arch_triple = packet.Peek(); +        ArchSpec arch_spec(arch_triple,NULL); +        m_process_launch_info.SetArchitecture(arch_spec); +        return SendOKResponse(); +    } +    return SendErrorResponse(12);  }  bool @@ -792,7 +997,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet          m_process_launch_info.AppendFileAction(file_action);          return SendOKResponse ();      } -    return SendErrorResponse (10); +    return SendErrorResponse (13);  }  bool @@ -809,7 +1014,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packe          m_process_launch_info.AppendFileAction(file_action);          return SendOKResponse ();      } -    return SendErrorResponse (11); +    return SendErrorResponse (14);  }  bool @@ -826,7 +1031,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packe          m_process_launch_info.AppendFileAction(file_action);          return SendOKResponse ();      } -    return SendErrorResponse (12); +    return SendErrorResponse (15);  }  bool @@ -837,3 +1042,277 @@ GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote &      m_send_acks = false;      return true;  } + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("qPlatform_IO_MkDir:")); +    mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); +    if (packet.GetChar() != ',') +        return false; +    std::string path; +    packet.GetHexByteString(path); +    uint32_t retcode = Host::MakeDirectory(path.c_str(),mode); +    StreamString response; +    response.PutHex32(retcode); +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("vFile:open:")); +    std::string path; +    packet.GetHexByteStringTerminatedBy(path,','); +    if (path.size() == 0) +        return false; +    if (packet.GetChar() != ',') +        return false; +    uint32_t flags = packet.GetHexMaxU32(false, UINT32_MAX); +    if (packet.GetChar() != ',') +        return false; +    mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); +    Error error; +    int fd = ::open (path.c_str(), flags, mode); +    const int save_errno = fd == -1 ? errno : 0; +    StreamString response; +    response.PutChar('F'); +    response.Printf("%i", fd); +    if (save_errno) +        response.Printf(",%i", save_errno); +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Close (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("vFile:close:")); +    int fd = packet.GetS32(-1); +    Error error; +    int err = -1; +    int save_errno = 0; +    if (fd >= 0) +    { +        err = close(fd); +        save_errno = err == -1 ? errno : 0; +    } +    else +    { +        save_errno = EINVAL; +    } +    StreamString response; +    response.PutChar('F'); +    response.Printf("%i", err); +    if (save_errno) +        response.Printf(",%i", save_errno); +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_pRead (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 +    // Not implemented on Windows +    return false; +#else +    StreamGDBRemote response; +    packet.SetFilePos(::strlen("vFile:pread:")); +    int fd = packet.GetS32(-1); +    if (packet.GetChar() != ',') +        return false; +    uint64_t count = packet.GetU64(UINT64_MAX); +    if (packet.GetChar() != ',') +        return false; +    uint64_t offset = packet.GetU64(UINT32_MAX); +    if (count == UINT64_MAX) +    { +        response.Printf("F-1:%i", EINVAL); +        SendPacketNoLock(response.GetData(), response.GetSize()); +        return true; +    } +    std::string buffer(count, 0); +    const ssize_t bytes_read = ::pread (fd, &buffer[0], buffer.size(), offset); +    const int save_errno = bytes_read == -1 ? errno : 0; +    response.PutChar('F'); +    response.Printf("%zi", bytes_read); +    if (save_errno) +        response.Printf(",%i", save_errno); +    else +    { +        response.PutChar(';'); +        response.PutEscapedBytes(&buffer[0], bytes_read); +    } +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +#endif +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 +    // Not implemented on Windows +    return false; +#else +    packet.SetFilePos(::strlen("vFile:pwrite:")); + +    StreamGDBRemote response; +    response.PutChar('F'); + +    int fd = packet.GetU32(UINT32_MAX); +    if (packet.GetChar() != ',') +        return false; +    off_t offset = packet.GetU64(UINT32_MAX); +    if (packet.GetChar() != ',') +        return false; +    std::string buffer; +    if (packet.GetEscapedBinaryData(buffer)) +    { +        const ssize_t bytes_written = ::pwrite (fd, buffer.data(), buffer.size(), offset); +        const int save_errno = bytes_written == -1 ? errno : 0; +        response.Printf("%zi", bytes_written); +        if (save_errno) +            response.Printf(",%i", save_errno); +    } +    else +    { +        response.Printf ("-1,%i", EINVAL); +    } + +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +#endif +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Size (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("vFile:size:")); +    std::string path; +    packet.GetHexByteString(path); +    if (path.empty()) +        return false; +    lldb::user_id_t retcode = Host::GetFileSize(FileSpec(path.c_str(), false)); +    StreamString response; +    response.PutChar('F'); +    response.PutHex64(retcode); +    if (retcode == UINT64_MAX) +    { +        response.PutChar(','); +        response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode() +    } +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Mode (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("vFile:mode:")); +    std::string path; +    packet.GetHexByteString(path); +    if (path.empty()) +        return false; +    Error error; +    const uint32_t mode = File::GetPermissions(path.c_str(), error); +    StreamString response; +    response.Printf("F%u", mode); +    if (mode == 0 || error.Fail()) +        response.Printf(",%i", (int)error.GetError()); +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Exists (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("vFile:exists:")); +    std::string path; +    packet.GetHexByteString(path); +    if (path.empty()) +        return false; +    bool retcode = Host::GetFileExists(FileSpec(path.c_str(), false)); +    StreamString response; +    response.PutChar('F'); +    response.PutChar(','); +    if (retcode) +        response.PutChar('1'); +    else +        response.PutChar('0'); +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("qPlatform_RunCommand:")); +    std::string path; +    std::string working_dir; +    packet.GetHexByteStringTerminatedBy(path,','); +    if (path.size() == 0) +        return false; +    if (packet.GetChar() != ',') +        return false; +    // FIXME: add timeout to qPlatform_RunCommand packet +    // uint32_t timeout = packet.GetHexMaxU32(false, 32); +    uint32_t timeout = 10; +    if (packet.GetChar() == ',') +        packet.GetHexByteString(working_dir); +    int status, signo; +    std::string output; +    Error err = Host::RunShellCommand(path.c_str(), +                                      working_dir.empty() ? NULL : working_dir.c_str(), +                                      &status, &signo, &output, timeout); +    StreamGDBRemote response; +    if (err.Fail()) +    { +        response.PutCString("F,"); +        response.PutHex32(UINT32_MAX); +    } +    else +    { +        response.PutCString("F,"); +        response.PutHex32(status); +        response.PutChar(','); +        response.PutHex32(signo); +        response.PutChar(','); +        response.PutEscapedBytes(output.c_str(), output.size()); +    } +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Stat (StringExtractorGDBRemote &packet) +{ +    return false; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_MD5 (StringExtractorGDBRemote &packet) +{ +    packet.SetFilePos(::strlen("vFile:exists:")); +    std::string path; +    packet.GetHexByteString(path); +    if (path.size() == 0) +        return false; +    uint64_t a,b; +    StreamGDBRemote response; +    if (Host::CalculateMD5(FileSpec(path.c_str(),false),a,b) == false) +    { +        response.PutCString("F,"); +        response.PutCString("x"); +    } +    else +    { +        response.PutCString("F,"); +        response.PutHex64(a); +        response.PutHex64(b); +    } +    SendPacketNoLock(response.GetData(), response.GetSize()); +    return true; +} | 
