#include "miprotocol.h"
#include "vscodeprotocol.h"
+static const uint16_t DEFAULT_SERVER_PORT = 4711;
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
);
}
bool engineLogging = false;
std::string logFilePath;
+ uint16_t serverPort = 0;
+
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--attach") == 0)
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]);
}
}
+ IORedirectServer server(serverPort);
ManagedDebugger debugger;
std::unique_ptr<Protocol> protocol;
#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>
{
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);
+}