Add option for listening requests on the TCP/IP port instead of stdin/out
authorIgor Kulaychuk <i.kulaychuk@samsung.com>
Wed, 11 Apr 2018 17:34:24 +0000 (20:34 +0300)
committerIgor Kulaychuk <i.kulaychuk@samsung.com>
Fri, 27 Apr 2018 17:49:15 +0000 (20:49 +0300)
src/debug/netcoredbg/main.cpp
src/debug/netcoredbg/platform.cpp
src/debug/netcoredbg/platform.h

index 919b49e..8bd8135 100644 (file)
@@ -12,6 +12,7 @@
 #include "miprotocol.h"
 #include "vscodeprotocol.h"
 
+static const uint16_t DEFAULT_SERVER_PORT = 4711;
 
 static void print_help()
 {
@@ -24,6 +25,10 @@ static void print_help()
         "--interpreter=vscode                  Puts the debugger into VS Code Debugger mode.\n"
         "--engineLogging[=<path to log file>]  Enable logging to VsDbg-UI or file for the engine.\n"
         "                                      Only supported by the VsCode interpreter.\n"
+        "--server[=port_num]                   Start the debugger listening for requests on the\n"
+        "                                      specified TCP/IP port instead of stdin/out. If port is not specified\n"
+        "                                      TCP %i will be used.\n",
+        (int)DEFAULT_SERVER_PORT
     );
 }
 
@@ -40,6 +45,8 @@ int main(int argc, char *argv[])
     bool engineLogging = false;
     std::string logFilePath;
 
+    uint16_t serverPort = 0;
+
     for (int i = 1; i < argc; i++)
     {
         if (strcmp(argv[i], "--attach") == 0)
@@ -84,6 +91,22 @@ int main(int argc, char *argv[])
             print_help();
             return EXIT_SUCCESS;
         }
+        else if (strcmp(argv[i], "--server") == 0)
+        {
+            serverPort = DEFAULT_SERVER_PORT;
+            continue;
+        }
+        else if (strstr(argv[i], "--server=") == argv[i])
+        {
+            char *err;
+            serverPort = strtoul(argv[i] + strlen("--server="), &err, 10);
+            if (*err != 0)
+            {
+                fprintf(stderr, "Error: Missing process id\n");
+                return EXIT_FAILURE;
+            }
+            continue;
+        }
         else
         {
             fprintf(stderr, "Error: Unknown option %s\n", argv[i]);
@@ -91,6 +114,7 @@ int main(int argc, char *argv[])
         }
     }
 
+    IORedirectServer server(serverPort);
     ManagedDebugger debugger;
     std::unique_ptr<Protocol> protocol;
 
index b48ef6e..1482f1d 100644 (file)
@@ -12,6 +12,9 @@
 #include <sys/stat.h>
 #include <dlfcn.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #if defined(__APPLE__)
 #include <mach-o/dyld.h>
@@ -182,3 +185,155 @@ void *DLSym(void *handle, const std::string &name)
 {
     return dlsym(handle, name.c_str());
 }
+
+// From https://stackoverflow.com/questions/13541313/handle-socket-descriptors-like-file-descriptor-fstream-c-linux
+class fdbuf : public std::streambuf
+{
+private:
+    enum { bufsize = 1024 };
+    char outbuf_[bufsize];
+    char inbuf_[bufsize + 16 - sizeof(int)];
+    int  fd_;
+public:
+    typedef std::streambuf::traits_type traits_type;
+
+    fdbuf(int fd);
+    ~fdbuf();
+    void open(int fd);
+    void close();
+
+protected:
+    int overflow(int c);
+    int underflow();
+    int sync();
+};
+
+fdbuf::fdbuf(int fd)
+  : fd_(-1) {
+    this->open(fd);
+}
+
+fdbuf::~fdbuf() {
+    this->close();
+}
+
+void fdbuf::open(int fd) {
+    this->close();
+    this->fd_ = fd;
+    this->setg(this->inbuf_, this->inbuf_, this->inbuf_);
+    this->setp(this->outbuf_, this->outbuf_ + bufsize - 1);
+}
+
+void fdbuf::close() {
+    if (!(this->fd_ < 0)) {
+        this->sync();
+        ::close(this->fd_);
+    }
+}
+
+int fdbuf::overflow(int c) {
+    if (!traits_type::eq_int_type(c, traits_type::eof())) {
+        *this->pptr() = traits_type::to_char_type(c);
+        this->pbump(1);
+    }
+    return this->sync() == -1
+        ? traits_type::eof()
+        : traits_type::not_eof(c);
+}
+
+int fdbuf::sync() {
+    if (this->pbase() != this->pptr()) {
+        std::streamsize size(this->pptr() - this->pbase());
+        std::streamsize done(::write(this->fd_, this->outbuf_, size));
+        // The code below assumes that it is success if the stream made
+        // some progress. Depending on the needs it may be more
+        // reasonable to consider it a success only if it managed to
+        // write the entire buffer and, e.g., loop a couple of times
+        // to try achieving this success.
+        if (0 < done) {
+            std::copy(this->pbase() + done, this->pptr(), this->pbase());
+            this->setp(this->pbase(), this->epptr());
+            this->pbump(size - done);
+        }
+    }
+    return this->pptr() != this->epptr()? 0: -1;
+}
+
+int fdbuf::underflow()
+{
+    if (this->gptr() == this->egptr()) {
+        std::streamsize pback(std::min(this->gptr() - this->eback(),
+                                       std::ptrdiff_t(16 - sizeof(int))));
+        std::copy(this->egptr() - pback, this->egptr(), this->eback());
+        int done(::read(this->fd_, this->eback() + pback, bufsize));
+        this->setg(this->eback(),
+                   this->eback() + pback,
+                   this->eback() + pback + std::max(0, done));
+    }
+    return this->gptr() == this->egptr()
+        ? traits_type::eof()
+        : traits_type::to_int_type(*this->gptr());
+}
+
+IORedirectServer::IORedirectServer(uint16_t port) : m_in(nullptr), m_out(nullptr), m_sockfd(-1)
+{
+    int newsockfd;
+    socklen_t clilen;
+    struct sockaddr_in serv_addr, cli_addr;
+    int n;
+
+    m_prevIn = std::cin.rdbuf();
+    m_prevOut = std::cout.rdbuf();
+
+    if (port == 0)
+        return;
+
+    m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
+    if (m_sockfd < 0)
+        return;
+
+    int enable = 1;
+    if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
+    {
+        ::close(m_sockfd);
+        m_sockfd = -1;
+        return;
+    }
+    memset(&serv_addr, 0, sizeof(serv_addr));
+
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = INADDR_ANY;
+    serv_addr.sin_port = htons(port);
+
+    if (::bind(m_sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+    {
+        ::close(m_sockfd);
+        m_sockfd = -1;
+        return;
+    }
+
+    ::listen(m_sockfd, 5);
+    clilen = sizeof(cli_addr);
+    newsockfd = ::accept(m_sockfd, (struct sockaddr *) &cli_addr, &clilen);
+    if (newsockfd < 0)
+    {
+        ::close(m_sockfd);
+        m_sockfd = -1;
+        return;
+    }
+
+    m_in = new fdbuf(newsockfd);
+    m_out = new fdbuf(newsockfd);
+
+    std::cin.rdbuf(m_in);
+    std::cout.rdbuf(m_out);
+}
+
+IORedirectServer::~IORedirectServer()
+{
+    std::cin.rdbuf(m_prevIn);
+    std::cout.rdbuf(m_prevOut);
+    delete m_in;
+    delete m_out;
+    ::close(m_sockfd);
+}
index ee84cc0..801bf4f 100644 (file)
@@ -4,6 +4,7 @@
 #pragma once
 
 #include <string>
+#include <iostream>
 
 unsigned long OSPageSize();
 void AddFilesFromDirectoryToTpaList(const std::string &directory, std::string &tpaList);
@@ -13,3 +14,16 @@ bool SetWorkDir(const std::string &path);
 void USleep(uint32_t duration);
 void *DLOpen(const std::string &path);
 void *DLSym(void *handle, const std::string &name);
+
+class IORedirectServer
+{
+    std::streambuf *m_in;
+    std::streambuf *m_out;
+    std::streambuf *m_prevIn;
+    std::streambuf *m_prevOut;
+    int m_sockfd;
+public:
+    IORedirectServer(uint16_t port);
+    ~IORedirectServer();
+    operator bool() const { return m_sockfd != -1; }
+};