[Lldb-commits] [lldb] r355637 - [lldb-vscode] Support running in server mode on Windows.

Zachary Turner via lldb-commits lldb-commits at lists.llvm.org
Thu Mar 7 13:23:21 PST 2019


Author: zturner
Date: Thu Mar  7 13:23:21 2019
New Revision: 355637

URL: http://llvm.org/viewvc/llvm-project?rev=355637&view=rev
Log:
[lldb-vscode] Support running in server mode on Windows.

Windows can't use standard i/o system calls such as read and write
to work with sockets, it instead needs to use the specific send
and recv calls.  This complicates matters for the debug adapter,
since it needs to be able to work in both server mode where it
communicates over a socket, as well as non-server mode where it
communicates via stdin and stdout.  To abstract this out, I've
introduced a class IOStream which hides all these details and
exposes a read/write interface that does the right on each
platform.

Differential Revision: https://reviews.llvm.org/D59104

Added:
    lldb/trunk/tools/lldb-vscode/IOStream.cpp
    lldb/trunk/tools/lldb-vscode/IOStream.h
Modified:
    lldb/trunk/tools/lldb-vscode/CMakeLists.txt
    lldb/trunk/tools/lldb-vscode/VSCode.cpp
    lldb/trunk/tools/lldb-vscode/VSCode.h
    lldb/trunk/tools/lldb-vscode/lldb-vscode.cpp
    lldb/trunk/tools/lldb-vscode/package.json

Modified: lldb/trunk/tools/lldb-vscode/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/CMakeLists.txt?rev=355637&r1=355636&r2=355637&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-vscode/CMakeLists.txt (original)
+++ lldb/trunk/tools/lldb-vscode/CMakeLists.txt Thu Mar  7 13:23:21 2019
@@ -15,6 +15,7 @@ add_lldb_tool(lldb-vscode
   BreakpointBase.cpp
   ExceptionBreakpoint.cpp
   FunctionBreakpoint.cpp
+  IOStream.cpp
   JSONUtils.cpp
   LLDBUtils.cpp
   SourceBreakpoint.cpp

Added: lldb/trunk/tools/lldb-vscode/IOStream.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/IOStream.cpp?rev=355637&view=auto
==============================================================================
--- lldb/trunk/tools/lldb-vscode/IOStream.cpp (added)
+++ lldb/trunk/tools/lldb-vscode/IOStream.cpp Thu Mar  7 13:23:21 2019
@@ -0,0 +1,153 @@
+//===-- IOStream.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IOStream.h"
+
+#if defined(_WIN32)
+#include <io.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+using namespace lldb_vscode;
+
+StreamDescriptor::StreamDescriptor() {}
+
+StreamDescriptor::StreamDescriptor(StreamDescriptor &&other) {
+  *this = std::move(other);
+}
+
+StreamDescriptor::~StreamDescriptor() {
+  if (!m_close)
+    return;
+
+  if (m_is_socket)
+#if defined(_WIN32)
+    ::closesocket(m_socket);
+#else
+    ::close(m_socket);
+#endif
+  else
+    ::close(m_fd);
+}
+
+StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) {
+  m_close = other.m_close;
+  other.m_close = false;
+  m_is_socket = other.m_is_socket;
+  if (m_is_socket)
+    m_socket = other.m_socket;
+  else
+    m_fd = other.m_fd;
+  return *this;
+}
+
+StreamDescriptor StreamDescriptor::from_socket(SOCKET s, bool close) {
+  StreamDescriptor sd;
+  sd.m_is_socket = true;
+  sd.m_socket = s;
+  sd.m_close = close;
+  return sd;
+}
+
+StreamDescriptor StreamDescriptor::from_file(int fd, bool close) {
+  StreamDescriptor sd;
+  sd.m_is_socket = false;
+  sd.m_fd = fd;
+  sd.m_close = close;
+  return sd;
+}
+
+bool OutputStream::write_full(llvm::StringRef str) {
+  while (!str.empty()) {
+    int bytes_written = 0;
+    if (descriptor.m_is_socket)
+      bytes_written = ::send(descriptor.m_socket, str.data(), str.size(), 0);
+    else
+      bytes_written = ::write(descriptor.m_fd, str.data(), str.size());
+
+    if (bytes_written < 0) {
+      if (errno == EINTR || errno == EAGAIN)
+        continue;
+      return false;
+    }
+    str = str.drop_front(bytes_written);
+  }
+
+  return true;
+}
+
+bool InputStream::read_full(std::ofstream *log, size_t length,
+                            std::string &text) {
+  std::string data;
+  data.resize(length);
+
+  char *ptr = &data[0];
+  while (length != 0) {
+    int bytes_read = 0;
+    if (descriptor.m_is_socket)
+      bytes_read = ::recv(descriptor.m_socket, ptr, length, 0);
+    else
+      bytes_read = ::read(descriptor.m_fd, ptr, length);
+
+    if (bytes_read < 0) {
+      int reason = 0;
+#if defined(_WIN32)
+      if (descriptor.m_is_socket)
+        reason = WSAGetLastError();
+      else
+        reason = errno;
+#else
+      reason = errno;
+      if (reason == EINTR || reason == EAGAIN)
+        continue;
+#endif
+
+      if (log)
+        *log << "Error " << reason << " reading from input file.\n";
+      return false;
+    }
+
+    assert(bytes_read <= length);
+    ptr += bytes_read;
+    length -= bytes_read;
+  }
+  text += data;
+  return true;
+}
+
+bool InputStream::read_line(std::ofstream *log, std::string &line) {
+  line.clear();
+  while (true) {
+    if (!read_full(log, 1, line))
+      return false;
+
+    if (llvm::StringRef(line).endswith("\r\n"))
+      break;
+  }
+  line.erase(line.size() - 2);
+  return true;
+}
+
+bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
+  std::string result;
+  if (!read_full(log, expected.size(), result))
+    return false;
+  if (expected != result) {
+    if (log)
+      *log << "Warning: Expected '" << expected.str() << "', got '" << result
+           << "\n";
+  }
+  return true;
+}

Added: lldb/trunk/tools/lldb-vscode/IOStream.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/IOStream.h?rev=355637&view=auto
==============================================================================
--- lldb/trunk/tools/lldb-vscode/IOStream.h (added)
+++ lldb/trunk/tools/lldb-vscode/IOStream.h Thu Mar  7 13:23:21 2019
@@ -0,0 +1,69 @@
+//===-- IOStream.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDBVSCODE_IOSTREAM_H_
+#define LLDBVSCODE_IOSTREAM_H_
+
+#if defined(_WIN32)
+// We need to #define NOMINMAX in order to skip `min()` and `max()` macro
+// definitions that conflict with other system headers.
+// We also need to #undef GetObject (which is defined to GetObjectW) because
+// the JSON code we use also has methods named `GetObject()` and we conflict
+// against these.
+#define NOMINMAX
+#include <windows.h>
+#else
+typedef int SOCKET;
+#endif
+
+#include "llvm/ADT/StringRef.h"
+
+#include <fstream>
+#include <string>
+
+// Windows requires different system calls for dealing with sockets and other
+// types of files, so we can't simply have one code path that just uses read
+// and write everywhere.  So we need an abstraction in order to allow us to
+// treat them identically.
+namespace lldb_vscode {
+struct StreamDescriptor {
+  StreamDescriptor();
+  ~StreamDescriptor();
+  StreamDescriptor(StreamDescriptor &&other);
+
+  StreamDescriptor &operator=(StreamDescriptor &&other);
+
+  static StreamDescriptor from_socket(SOCKET s, bool close);
+  static StreamDescriptor from_file(int fd, bool close);
+
+  bool m_is_socket = false;
+  bool m_close = false;
+  union {
+    int m_fd;
+    SOCKET m_socket;
+  };
+};
+
+struct InputStream {
+  StreamDescriptor descriptor;
+
+  bool read_full(std::ofstream *log, size_t length, std::string &text);
+
+  bool read_line(std::ofstream *log, std::string &line);
+
+  bool read_expected(std::ofstream *log, llvm::StringRef expected);
+};
+
+struct OutputStream {
+  StreamDescriptor descriptor;
+
+  bool write_full(llvm::StringRef str);
+};
+} // namespace lldb_vscode
+
+#endif

Modified: lldb/trunk/tools/lldb-vscode/VSCode.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/VSCode.cpp?rev=355637&r1=355636&r2=355637&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-vscode/VSCode.cpp (original)
+++ lldb/trunk/tools/lldb-vscode/VSCode.cpp Thu Mar  7 13:23:21 2019
@@ -10,12 +10,15 @@
 #include <fstream>
 #include <mutex>
 
-#include "VSCode.h"
 #include "LLDBUtils.h"
+#include "VSCode.h"
+#include "llvm/Support/FormatVariadic.h"
 
 #if defined(_WIN32)
-#include <io.h>
+#define NOMINMAX
+#include <Windows.h>
 #include <fcntl.h>
+#include <io.h>
 #endif
 
 using namespace lldb_vscode;
@@ -31,15 +34,15 @@ namespace lldb_vscode {
 VSCode g_vsc;
 
 VSCode::VSCode()
-    : in(stdin), out(stdout), launch_info(nullptr), variables(),
-      broadcaster("lldb-vscode"), num_regs(0), num_locals(0), num_globals(0),
-      log(), exception_breakpoints(
-                 {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
-                  {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
-                  {"objc_catch", "Objective C Catch", lldb::eLanguageTypeObjC},
-                  {"objc_throw", "Objective C Throw", lldb::eLanguageTypeObjC},
-                  {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
-                  {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
+    : launch_info(nullptr), variables(), broadcaster("lldb-vscode"),
+      num_regs(0), num_locals(0), num_globals(0), log(),
+      exception_breakpoints(
+          {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
+           {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
+           {"objc_catch", "Objective C Catch", lldb::eLanguageTypeObjC},
+           {"objc_throw", "Objective C Throw", lldb::eLanguageTypeObjC},
+           {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
+           {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
       focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
       stop_at_entry(false) {
   const char *log_file_path = getenv("LLDBVSCODE_LOG");
@@ -55,22 +58,6 @@ VSCode::VSCode()
 }
 
 VSCode::~VSCode() {
-  CloseInputStream();
-  CloseOutputStream();
-}
-
-void VSCode::CloseInputStream() {
-  if (in != stdin) {
-    fclose(in);
-    in = nullptr;
-  }
-}
-
-void VSCode::CloseOutputStream() {
-  if (out != stdout) {
-    fclose(out);
-    out = nullptr;
-  }
 }
 
 int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
@@ -103,9 +90,11 @@ VSCode::GetExceptionBreakpoint(const lld
 // JSON bytes.
 //----------------------------------------------------------------------
 void VSCode::SendJSON(const std::string &json_str) {
-  fprintf(out, "Content-Length: %u\r\n\r\n%s", (uint32_t)json_str.size(),
-          json_str.c_str());
-  fflush(out);
+  output.write_full("Content-Length: ");
+  output.write_full(llvm::utostr(json_str.size()));
+  output.write_full("\r\n\r\n");
+  output.write_full(json_str);
+
   if (log) {
     *log << "<-- " << std::endl
          << "Content-Length: " << json_str.size() << "\r\n\r\n"
@@ -130,44 +119,25 @@ void VSCode::SendJSON(const llvm::json::
 // Read a JSON packet from the "in" stream.
 //----------------------------------------------------------------------
 std::string VSCode::ReadJSON() {
-  static std::string header("Content-Length: ");
-
-  uint32_t packet_len = 0;
+  std::string length_str;
   std::string json_str;
-  char line[1024];
+  int length;
+
+  if (!input.read_expected(log.get(), "Content-Length: "))
+    return json_str;
+
+  if (!input.read_line(log.get(), length_str))
+    return json_str;
+
+  if (!llvm::to_integer(length_str, length))
+    return json_str;
+
+  if (!input.read_expected(log.get(), "\r\n"))
+    return json_str;
+
+  if (!input.read_full(log.get(), length, json_str))
+    return json_str;
 
-  while (fgets(line, sizeof(line), in)) {
-    if (strncmp(line, header.data(), header.size()) == 0) {
-      packet_len = atoi(line + header.size());
-      if (fgets(line, sizeof(line), in)) {
-        if (!IsEmptyLine(line))
-          if (log)
-            *log << "warning: expected empty line but got: \"" << line << "\""
-                 << std::endl;
-        break;
-      }
-    } else {
-      if (log)
-        *log << "warning: expected \"" << header << "\" but got: \"" << line
-             << "\"" << std::endl;
-    }
-  }
-  // This is followed by two windows newline sequences ("\r\n\r\n") so eat
-  // two the newline sequences
-  if (packet_len > 0) {
-    json_str.resize(packet_len);
-    auto bytes_read = fread(&json_str[0], 1, packet_len, in);
-    if (bytes_read < packet_len) {
-      if (log)
-        *log << "error: read fewer bytes (" << bytes_read
-             << ") than requested (" << packet_len << ")" << std::endl;
-      json_str.erase(bytes_read);
-    }
-    if (log) {
-      *log << "--> " << std::endl;
-      *log << header << packet_len << "\r\n\r\n" << json_str << std::endl;
-    }
-  }
   return json_str;
 }
 

Modified: lldb/trunk/tools/lldb-vscode/VSCode.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/VSCode.h?rev=355637&r1=355636&r2=355637&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-vscode/VSCode.h (original)
+++ lldb/trunk/tools/lldb-vscode/VSCode.h Thu Mar  7 13:23:21 2019
@@ -19,6 +19,7 @@
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
 
 #include "lldb/API/SBAttachInfo.h"
 #include "lldb/API/SBBreakpoint.h"
@@ -43,6 +44,7 @@
 
 #include "ExceptionBreakpoint.h"
 #include "FunctionBreakpoint.h"
+#include "IOStream.h"
 #include "SourceBreakpoint.h"
 #include "SourceReference.h"
 
@@ -62,8 +64,8 @@ typedef llvm::StringMap<FunctionBreakpoi
 enum class OutputType { Console, Stdout, Stderr, Telemetry };
 
 struct VSCode {
-  FILE *in;
-  FILE *out;
+  InputStream input;
+  OutputStream output;
   lldb::SBDebugger debugger;
   lldb::SBTarget target;
   lldb::SBAttachInfo attach_info;
@@ -94,8 +96,6 @@ struct VSCode {
   ~VSCode();
   VSCode(const VSCode &rhs) = delete;
   void operator=(const VSCode &rhs) = delete;
-  void CloseInputStream();
-  void CloseOutputStream();
   int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const;
   ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
   ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);

Modified: lldb/trunk/tools/lldb-vscode/lldb-vscode.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/lldb-vscode.cpp?rev=355637&r1=355636&r2=355637&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-vscode/lldb-vscode.cpp (original)
+++ lldb/trunk/tools/lldb-vscode/lldb-vscode.cpp Thu Mar  7 13:23:21 2019
@@ -23,6 +23,7 @@
 #define NOMINMAX
 #include <windows.h>
 #undef GetObject
+#include <io.h>
 #else
 #include <netinet/in.h>
 #include <sys/socket.h>
@@ -53,7 +54,6 @@ typedef int socklen_t;
 constexpr const char *dev_null_path = "nul";
 
 #else
-typedef int SOCKET;
 constexpr const char *dev_null_path = "/dev/null";
 
 #endif
@@ -68,9 +68,9 @@ enum LaunchMethod { Launch, Attach, Atta
 
 enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 };
 
-int AcceptConnection(int portno) {
+SOCKET AcceptConnection(int portno) {
   // Accept a socket connection from any host on "portno".
-  int newsockfd = -1;
+  SOCKET newsockfd = -1;
   struct sockaddr_in serv_addr, cli_addr;
   SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if (sockfd < 0) {
@@ -2635,23 +2635,19 @@ int main(int argc, char *argv[]) {
 #endif
       int portno = atoi(arg);
       printf("Listening on port %i...\n", portno);
-      int socket_fd = AcceptConnection(portno);
+      SOCKET socket_fd = AcceptConnection(portno);
       if (socket_fd >= 0) {
-        // We must open two FILE objects, one for reading and one for writing
-        // the FILE objects have a mutex in them that won't allow reading and
-        // writing to the socket stream.
-        g_vsc.in = fdopen(socket_fd, "r");
-        g_vsc.out = fdopen(socket_fd, "w");
-        if (g_vsc.in == nullptr || g_vsc.out == nullptr) {
-          if (g_vsc.log)
-            *g_vsc.log << "fdopen failed (" << strerror(errno) << ")"
-                       << std::endl;
-          exit(1);
-        }
+        g_vsc.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
+        g_vsc.output.descriptor =
+            StreamDescriptor::from_socket(socket_fd, false);
       } else {
         exit(1);
       }
     }
+  } else {
+    g_vsc.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
+    g_vsc.output.descriptor =
+        StreamDescriptor::from_file(fileno(stdout), false);
   }
   auto request_handlers = GetRequestHandlers();
   uint32_t packet_idx = 0;

Modified: lldb/trunk/tools/lldb-vscode/package.json
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-vscode/package.json?rev=355637&r1=355636&r2=355637&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-vscode/package.json (original)
+++ lldb/trunk/tools/lldb-vscode/package.json Thu Mar  7 13:23:21 2019
@@ -56,6 +56,9 @@
 					]
 				},
 				"program": "./bin/lldb-vscode",
+				"windows": {
+					"program": "./bin/lldb-vscode.exe"
+				},
 				"configurationAttributes": {
 					"launch": {
 						"required": [




More information about the lldb-commits mailing list