[Lldb-commits] [lldb] [lldb] Add PlatformWebInspectorWasm (PR #188751)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Thu Mar 26 07:14:04 PDT 2026


https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/188751

Add a new PlatformWebInspectorWasm, which is a Wasm platform that automatically connects to the WebInspector platform server.

>From 8012c9569af7f329a58342afbeb983e787a93133 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 24 Mar 2026 17:36:21 -0500
Subject: [PATCH] [lldb] Add PlatformWebInspectorWasm

Add a new PlatformWebInspectorWasm, which is a Wasm platform that
automatically connects to the WebInspector platform server.
---
 .../Platform/WebAssembly/CMakeLists.txt       |   1 +
 .../Platform/WebAssembly/PlatformWasm.cpp     |  34 ++--
 .../Platform/WebAssembly/PlatformWasm.h       |  13 +-
 .../WebAssembly/PlatformWebInspectorWasm.cpp  | 177 ++++++++++++++++++
 .../WebAssembly/PlatformWebInspectorWasm.h    |  58 ++++++
 5 files changed, 264 insertions(+), 19 deletions(-)
 create mode 100644 lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.cpp
 create mode 100644 lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.h

diff --git a/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt b/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt
index e049937dffd47..3149189cbe32c 100644
--- a/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt
+++ b/lldb/source/Plugins/Platform/WebAssembly/CMakeLists.txt
@@ -9,6 +9,7 @@ lldb_tablegen(PlatformWasmPropertiesEnum.inc -gen-lldb-property-enum-defs
 add_lldb_library(lldbPluginPlatformWasm PLUGIN
   PlatformWasm.cpp
   PlatformWasmRemoteGDBServer.cpp
+  PlatformWebInspectorWasm.cpp
 
   LINK_LIBS
     lldbCore
diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp
index bcc71f83a017d..c9af2551281e9 100644
--- a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp
+++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.cpp
@@ -8,6 +8,7 @@
 
 #include "Plugins/Platform/WebAssembly/PlatformWasm.h"
 #include "Plugins/Platform/WebAssembly/PlatformWasmRemoteGDBServer.h"
+#include "Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.h"
 #include "Plugins/Process/wasm/ProcessWasm.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Host/FileSystem.h"
@@ -71,6 +72,7 @@ llvm::StringRef PlatformWasm::GetPluginDescriptionStatic() {
 }
 
 void PlatformWasm::Initialize() {
+  PlatformWebInspectorWasm::Initialize();
   PluginManager::RegisterPlugin(
       GetPluginNameStatic(), GetPluginDescriptionStatic(),
       PlatformWasm::CreateInstance, PlatformWasm::DebuggerInitialize);
@@ -78,6 +80,7 @@ void PlatformWasm::Initialize() {
 
 void PlatformWasm::Terminate() {
   PluginManager::UnregisterPlugin(PlatformWasm::CreateInstance);
+  PlatformWebInspectorWasm::Terminate();
 }
 
 void PlatformWasm::DebuggerInitialize(Debugger &debugger) {
@@ -113,17 +116,20 @@ PlatformSP PlatformWasm::CreateInstance(bool force, const ArchSpec *arch) {
   return create ? PlatformSP(new PlatformWasm()) : PlatformSP();
 }
 
+llvm::Expected<uint16_t> PlatformWasm::FindFreeTCPPort() {
+  TCPSocket sock(true);
+  Status status = sock.Listen("localhost:0", /*backlog=*/5);
+  if (status.Fail())
+    return status.takeError();
+  return sock.GetLocalPortNumber();
+}
+
 std::vector<ArchSpec>
 PlatformWasm::GetSupportedArchitectures(const ArchSpec &process_host_arch) {
   return {ArchSpec("wasm32-unknown-unknown-wasm"),
           ArchSpec("wasm64-unknown-unknown-wasm")};
 }
 
-static auto get_arg_range(const Args &args) {
-  return llvm::make_range(args.GetArgumentArrayRef().begin(),
-                          args.GetArgumentArrayRef().end());
-}
-
 lldb::ProcessSP PlatformWasm::Attach(ProcessAttachInfo &attach_info,
                                      Debugger &debugger, Target *target,
                                      Status &status) {
@@ -155,18 +161,12 @@ lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info,
     return nullptr;
   }
 
-  uint16_t port = 0;
-  {
-    // Get the next available port by binding a socket to port 0.
-    TCPSocket listen_socket(true);
-    error = listen_socket.Listen("localhost:0", /*backlog=*/5);
-    if (error.Fail())
-      return nullptr;
-    port = listen_socket.GetLocalPortNumber();
-  }
-
-  if (error.Fail())
+  llvm::Expected<uint16_t> expected_port = FindFreeTCPPort();
+  if (!expected_port) {
+    error = Status::FromError(expected_port.takeError());
     return nullptr;
+  }
+  uint16_t port = *expected_port;
 
   Args args({runtime.GetPath(),
              llvm::formatv("{0}{1}", properties.GetPortArg(), port).str()});
@@ -192,7 +192,7 @@ lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info,
   llvm::Error Err = launch_info.SetUpPtyRedirection();
   LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
 
-  LLDB_LOG(log, "{0}", get_arg_range(launch_info.GetArguments()));
+  LLDB_LOG(log, "{0}", GetArgRange(launch_info.GetArguments()));
   error = Host::LaunchProcess(launch_info);
   if (error.Fail())
     return nullptr;
diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h
index 5744ca7d0ca22..e752b8bae9840 100644
--- a/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h
+++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWasm.h
@@ -55,11 +55,20 @@ class PlatformWasm : public RemoteAwarePlatform {
         arch, addr, length, prot, flags, fd, offset);
   }
 
+protected:
+  /// Find a free TCP port by binding to port 0.
+  static llvm::Expected<uint16_t> FindFreeTCPPort();
+
+  static auto GetArgRange(const Args &args) {
+    return llvm::make_range(args.GetArgumentArrayRef().begin(),
+                            args.GetArgumentArrayRef().end());
+  }
+
+  PlatformWasm() : RemoteAwarePlatform(/*is_host=*/false) {}
+
 private:
   static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
   static void DebuggerInitialize(Debugger &debugger);
-
-  PlatformWasm() : RemoteAwarePlatform(/*is_host=*/false) {}
 };
 
 } // namespace lldb_private
diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.cpp b/lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.cpp
new file mode 100644
index 0000000000000..fc60a98f46862
--- /dev/null
+++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.cpp
@@ -0,0 +1,177 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "PlatformWebInspectorWasm.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ErrorExtras.h"
+
+#include <chrono>
+#include <csignal>
+#include <thread>
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(PlatformWebInspectorWasm)
+
+static constexpr llvm::StringLiteral kServerBinary =
+    "/System/Cryptexes/App/usr/libexec/webinspector-wasm-lldb-platform";
+static constexpr uint8_t kConnectAttempts = 5;
+static constexpr auto kConnectDelay = std::chrono::milliseconds(100);
+
+llvm::StringRef PlatformWebInspectorWasm::GetPluginDescriptionStatic() {
+  return "Platform for debugging Wasm via WebInspector";
+}
+
+void PlatformWebInspectorWasm::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(),
+                                PlatformWebInspectorWasm::CreateInstance);
+}
+
+void PlatformWebInspectorWasm::Terminate() {
+  PluginManager::UnregisterPlugin(PlatformWebInspectorWasm::CreateInstance);
+}
+
+PlatformSP PlatformWebInspectorWasm::CreateInstance(bool force,
+                                                    const ArchSpec *arch) {
+  Log *log = GetLog(LLDBLog::Platform);
+  LLDB_LOG(log, "force = {0}, arch = ({1}, {2})", force,
+           arch ? arch->GetArchitectureName() : "<null>",
+           arch ? arch->GetTriple().getTriple() : "<null>");
+  return force ? PlatformSP(new PlatformWebInspectorWasm()) : PlatformSP();
+}
+
+PlatformWebInspectorWasm::~PlatformWebInspectorWasm() {
+  if (m_server_pid != LLDB_INVALID_PROCESS_ID)
+    Host::Kill(m_server_pid, SIGTERM);
+}
+
+llvm::Error PlatformWebInspectorWasm::LaunchPlatformServer() {
+  Log *log = GetLog(LLDBLog::Platform);
+
+  if (!FileSystem::Instance().Exists(FileSpec(kServerBinary)))
+    return llvm::createStringErrorV("platform binary not found: {0}",
+                                    kServerBinary);
+
+  // Find two free TCP ports.
+  llvm::Expected<uint16_t> expected_platform_port = FindFreeTCPPort();
+  if (!expected_platform_port)
+    return expected_platform_port.takeError();
+  uint16_t platform_port = *expected_platform_port;
+
+  llvm::Expected<uint16_t> expected_debugserver_port = FindFreeTCPPort();
+  if (!expected_debugserver_port)
+    return expected_debugserver_port.takeError();
+  uint16_t debugserver_port = *expected_debugserver_port;
+
+  ProcessLaunchInfo launch_info;
+  launch_info.SetExecutableFile(FileSpec(kServerBinary),
+                                /*add_exe_file_as_first_arg=*/true);
+  Args args;
+  args.AppendArgument(kServerBinary);
+  args.AppendArgument("--platform");
+  args.AppendArgument(llvm::utostr(platform_port));
+  args.AppendArgument("--debugserver");
+  args.AppendArgument(llvm::utostr(debugserver_port));
+  launch_info.SetArguments(args, /*first_arg_is_executable=*/true);
+  launch_info.SetLaunchInSeparateProcessGroup(true);
+  launch_info.GetFlags().Clear(eLaunchFlagDebug);
+
+  launch_info.SetMonitorProcessCallback(
+      [log](lldb::pid_t pid, int signal, int status) {
+        LLDB_LOG(log,
+                 "Platform exited: pid = {0}, signal = "
+                 "{1}, status = {2}",
+                 pid, signal, status);
+      });
+
+  LLDB_LOG(log, "{0}", GetArgRange(launch_info.GetArguments()));
+
+  Status status = Host::LaunchProcess(launch_info);
+  if (status.Fail())
+    return status.takeError();
+
+  m_server_pid = launch_info.GetProcessID();
+  LLDB_LOG(log, "Platform launched: pid = {0}", m_server_pid);
+
+  Args connect_args;
+  connect_args.AppendArgument(
+      llvm::formatv("connect://localhost:{0}", platform_port).str());
+
+  // The platform may need some time to bind a socket to the requested port.
+  for (uint8_t attempt = 0; attempt < kConnectAttempts; attempt++) {
+    status = PlatformWasm::ConnectRemote(connect_args);
+    if (status.Success())
+      return llvm::Error::success();
+
+    LLDB_LOG(
+        log,
+        "[{0}/{1}] platform not yet listening on port {2}: trying again in {3}",
+        attempt, kConnectAttempts, platform_port, kConnectDelay);
+    std::this_thread::sleep_for(kConnectDelay);
+  }
+  return status.takeError();
+}
+
+llvm::Error PlatformWebInspectorWasm::EnsureConnected() {
+  if (m_remote_platform_sp)
+    return llvm::Error::success();
+  return LaunchPlatformServer();
+}
+
+Status PlatformWebInspectorWasm::ConnectRemote(Args &args) {
+  if (args.GetArgumentCount() == 0)
+    return Status::FromError(LaunchPlatformServer());
+  return PlatformWasm::ConnectRemote(args);
+}
+
+ProcessSP PlatformWebInspectorWasm::Attach(ProcessAttachInfo &attach_info,
+                                           Debugger &debugger, Target *target,
+                                           Status &status) {
+  status = Status::FromError(EnsureConnected());
+  if (status.Fail())
+    return nullptr;
+  return PlatformWasm::Attach(attach_info, debugger, target, status);
+}
+
+uint32_t PlatformWebInspectorWasm::FindProcesses(
+    const ProcessInstanceInfoMatch &match_info,
+    ProcessInstanceInfoList &proc_infos) {
+  if (llvm::Error err = EnsureConnected()) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::Platform), std::move(err),
+                   "EnsureConnected failed: {0}");
+    return 0;
+  }
+  return PlatformWasm::FindProcesses(match_info, proc_infos);
+}
+
+bool PlatformWebInspectorWasm::GetProcessInfo(lldb::pid_t pid,
+                                              ProcessInstanceInfo &proc_info) {
+  if (llvm::Error err = EnsureConnected()) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::Platform), std::move(err),
+                   "EnsureConnected failed: {0}");
+    return false;
+  }
+  return PlatformWasm::GetProcessInfo(pid, proc_info);
+}
+
+lldb::ProcessSP
+PlatformWebInspectorWasm::DebugProcess(ProcessLaunchInfo &launch_info,
+                                       Debugger &debugger, Target &target,
+                                       Status &error) {
+  error = Status::FromErrorStringWithFormatv("{0} does not support launching",
+                                             GetPluginNameStatic());
+  return nullptr;
+}
diff --git a/lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.h b/lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.h
new file mode 100644
index 0000000000000..52286b4532ebc
--- /dev/null
+++ b/lldb/source/Plugins/Platform/WebAssembly/PlatformWebInspectorWasm.h
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 LLDB_SOURCE_PLUGINS_PLATFORM_WEBASSEMBLY_PLATFORMWEBINSPECTORWASM_H
+#define LLDB_SOURCE_PLUGINS_PLATFORM_WEBASSEMBLY_PLATFORMWEBINSPECTORWASM_H
+
+#include "PlatformWasm.h"
+
+namespace lldb_private {
+
+class PlatformWebInspectorWasm : public PlatformWasm {
+public:
+  static void Initialize();
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() { return "webinspector-wasm"; }
+  static llvm::StringRef GetPluginDescriptionStatic();
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+  llvm::StringRef GetDescription() override {
+    return GetPluginDescriptionStatic();
+  }
+
+  ~PlatformWebInspectorWasm() override;
+
+  Status ConnectRemote(Args &args) override;
+
+  lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+                         Target *target, Status &status) override;
+
+  lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info,
+                               Debugger &debugger, Target &target,
+                               Status &error) override;
+
+  uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
+                         ProcessInstanceInfoList &proc_infos) override;
+
+  bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override;
+
+private:
+  static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+  PlatformWebInspectorWasm() = default;
+
+  llvm::Error LaunchPlatformServer();
+  llvm::Error EnsureConnected();
+
+  lldb::pid_t m_server_pid = LLDB_INVALID_PROCESS_ID;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PLATFORM_WEBASSEMBLY_PLATFORMWEBINSPECTORWASM_H



More information about the lldb-commits mailing list