labath updated this revision to Diff 389443.
labath marked 4 inline comments as done.
labath added a comment.

Address Michał's feedback.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D114509/new/

https://reviews.llvm.org/D114509

Files:
  lldb/packages/Python/lldbsuite/test/gdbclientutils.py
  lldb/source/Plugins/Platform/CMakeLists.txt
  lldb/source/Plugins/Platform/Qemu/CMakeLists.txt
  lldb/source/Plugins/Platform/Qemu/PlatformQemuProperties.td
  lldb/source/Plugins/Platform/Qemu/PlatformQemuUser.cpp
  lldb/source/Plugins/Platform/Qemu/PlatformQemuUser.h
  lldb/test/API/qemu/Makefile
  lldb/test/API/qemu/TestQemuLaunch.py
  lldb/test/API/qemu/main.c
  lldb/test/API/qemu/qemu.py

Index: lldb/test/API/qemu/qemu.py
===================================================================
--- /dev/null
+++ lldb/test/API/qemu/qemu.py
@@ -0,0 +1,37 @@
+from textwrap import dedent
+import argparse
+import socket
+import json
+
+import use_lldb_suite
+from lldbsuite.test.gdbclientutils import *
+
+class MyResponder(MockGDBServerResponder):
+    def cont(self):
+        return "W47"
+
+class FakeEmulator(MockGDBServer):
+    def __init__(self, addr):
+        super().__init__(UnixServerSocket(addr))
+        self.responder = MyResponder()
+
+def main():
+    parser = argparse.ArgumentParser(description=dedent("""\
+            Implements a fake qemu for testing purposes. The executable program
+            is not actually run. Instead a very basic mock process is presented
+            to lldb.  The emulated program must accept at least one argument.
+            This should be a path where the emulator will dump its state. This
+            allows us to check the invocation parameters.
+            """))
+    parser.add_argument('-g', metavar="unix-socket", required=True)
+    parser.add_argument('program', help="The program to 'emulate'.")
+    parser.add_argument('state_file', help="Where to dump the emulator state.")
+    parsed, rest = parser.parse_known_args()
+    with open(parsed.state_file, "w") as f:
+        json.dump({"program":parsed.program, "rest":rest}, f)
+
+    emulator = FakeEmulator(parsed.g)
+    emulator.run()
+
+if __name__ == "__main__":
+    main()
Index: lldb/test/API/qemu/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/qemu/main.c
@@ -0,0 +1 @@
+int main() {}
Index: lldb/test/API/qemu/TestQemuLaunch.py
===================================================================
--- /dev/null
+++ lldb/test/API/qemu/TestQemuLaunch.py
@@ -0,0 +1,68 @@
+from __future__ import print_function
+import lldb
+import unittest
+import os
+import json
+import stat
+import sys
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+
+
+@skipIfRemote
+@skipIfWindows
+class TestQemuLaunch(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        super().setUp()
+        self.runCmd("settings set platform.plugin.qemu-user.architecture %s" %
+                self.getArchitecture())
+        emulator = self.getBuildArtifact("qemu.py")
+        with os.fdopen(os.open(emulator, os.O_WRONLY|os.O_CREAT, stat.S_IRWXU),
+                "w") as e:
+            e.write("#! %s\n"%sys.executable)
+            e.write("import runpy\n")
+            e.write("import sys\n")
+            e.write("sys.path = %r\n"%sys.path)
+            e.write("runpy.run_path(%r, run_name='__main__')\n"%
+                    self.getSourcePath("qemu.py"))
+        self.runCmd(
+                "settings set platform.plugin.qemu-user.emulator-path '%s'"%emulator)
+
+    def test_basic_launch(self):
+        self.build()
+        exe = self.getBuildArtifact()
+
+        error = lldb.SBError()
+        target = self.dbg.CreateTarget(exe, '', 'qemu-user', False, error)
+        self.assertSuccess(error)
+        self.assertEqual(target.GetPlatform().GetName(), "qemu-user")
+        process = target.LaunchSimple(
+                [self.getBuildArtifact("state.log"), "arg2", "arg3"], None, None)
+        self.assertIsNotNone(process)
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+        self.assertEqual(process.GetExitStatus(), 0x47)
+        with open(self.getBuildArtifact("state.log")) as s:
+            state = json.load(s)
+        self.assertEqual(state["program"], self.getBuildArtifact())
+        self.assertEqual(state["rest"], ["arg2", "arg3"])
+
+    def test_bad_emulator_path(self):
+        self.runCmd(
+                "settings set platform.plugin.qemu-user.emulator-path '%s'"%self.getBuildArtifact("nonexistent.file"))
+
+        self.build()
+        exe = self.getBuildArtifact()
+
+        error = lldb.SBError()
+        target = self.dbg.CreateTarget(exe, '', 'qemu-user', False, error)
+        self.assertEqual(target.GetPlatform().GetName(), "qemu-user")
+        self.assertSuccess(error)
+        info = lldb.SBLaunchInfo([])
+        target.Launch(info, error)
+        self.assertTrue(error.Fail())
+        self.assertIn("doesn't exist", error.GetCString())
Index: lldb/test/API/qemu/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/qemu/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/source/Plugins/Platform/Qemu/PlatformQemuUser.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Platform/Qemu/PlatformQemuUser.h
@@ -0,0 +1,57 @@
+//===-- PlatformQemuUser.h ------------------------------------------------===//
+//
+// 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 "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Platform.h"
+
+namespace lldb_private {
+
+class PlatformQemuUser : public Platform {
+public:
+  static void Initialize();
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() { return "qemu-user"; }
+  static llvm::StringRef GetPluginDescriptionStatic();
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+  llvm::StringRef GetDescription() override {
+    return GetPluginDescriptionStatic();
+  }
+
+  UserIDResolver &GetUserIDResolver() override {
+    return HostInfo::GetUserIDResolver();
+  }
+
+  std::vector<ArchSpec> GetSupportedArchitectures() override;
+
+  lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info,
+                               Debugger &debugger, Target &target,
+                               Status &error) override;
+
+  lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
+                         Target *target, Status &status) override {
+    status.SetErrorString("Not supported");
+    return nullptr;
+  }
+
+  bool IsConnected() const override { return true; }
+
+  void CalculateTrapHandlerSymbolNames() override {}
+
+  Environment GetEnvironment() override { return Host::GetEnvironment(); }
+
+private:
+  static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+  static void DebuggerInitialize(Debugger &debugger);
+
+  PlatformQemuUser() : Platform(/*is_host=*/false) {}
+};
+
+} // namespace lldb_private
Index: lldb/source/Plugins/Platform/Qemu/PlatformQemuUser.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Platform/Qemu/PlatformQemuUser.cpp
@@ -0,0 +1,148 @@
+//===-- PlatformQemuUser.cpp ----------------------------------------------===//
+//
+// 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 "Plugins/Platform/Qemu/PlatformQemuUser.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE_ADV(PlatformQemuUser, PlatformQemu)
+
+#define LLDB_PROPERTIES_platformqemuuser
+#include "PlatformQemuProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_platformqemuuser
+#include "PlatformQemuPropertiesEnum.inc"
+};
+
+class PluginProperties : public Properties {
+public:
+  PluginProperties() {
+    m_collection_sp = std::make_shared<OptionValueProperties>(
+        ConstString(PlatformQemuUser::GetPluginNameStatic()));
+    m_collection_sp->Initialize(g_platformqemuuser_properties);
+  }
+
+  llvm::StringRef GetArchitecture() {
+    return m_collection_sp->GetPropertyAtIndexAsString(
+        nullptr, ePropertyArchitecture, "");
+  }
+
+  FileSpec GetEmulatorPath() {
+    return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr,
+                                                         ePropertyEmulatorPath);
+  }
+};
+
+static PluginProperties &GetGlobalProperties() {
+  static PluginProperties g_settings;
+  return g_settings;
+}
+
+llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() {
+  return "Platform for debugging binaries under user mode qemu";
+}
+
+void PlatformQemuUser::Initialize() {
+  PluginManager::RegisterPlugin(
+      GetPluginNameStatic(), GetPluginDescriptionStatic(),
+      PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize);
+}
+
+void PlatformQemuUser::Terminate() {
+  PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance);
+}
+
+void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) {
+  if (!PluginManager::GetSettingForPlatformPlugin(
+          debugger, ConstString(GetPluginNameStatic()))) {
+    PluginManager::CreateSettingForPlatformPlugin(
+        debugger, GetGlobalProperties().GetValueProperties(),
+        ConstString("Properties for the qemu-user platform plugin."),
+        /*is_global_property=*/true);
+  }
+}
+
+PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) {
+  if (force)
+    return PlatformSP(new PlatformQemuUser());
+  return nullptr;
+}
+
+std::vector<ArchSpec> PlatformQemuUser::GetSupportedArchitectures() {
+  llvm::Triple triple = HostInfo::GetArchitecture().GetTriple();
+  triple.setEnvironment(llvm::Triple::UnknownEnvironment);
+  triple.setArchName(GetGlobalProperties().GetArchitecture());
+  if (triple.getArch() != llvm::Triple::UnknownArch)
+    return {ArchSpec(triple)};
+  return {};
+}
+
+static auto get_arg_range(const Args &args) {
+  return llvm::make_range(args.GetArgumentArrayRef().begin(),
+                          args.GetArgumentArrayRef().end());
+}
+
+lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info,
+                                               Debugger &debugger,
+                                               Target &target, Status &error) {
+  Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
+
+  std::string qemu = GetGlobalProperties().GetEmulatorPath().GetPath();
+
+  llvm::SmallString<0> socket_model, socket_path;
+  HostInfo::GetProcessTempDir().GetPath(socket_model);
+  llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket");
+  do {
+    llvm::sys::fs::createUniquePath(socket_model, socket_path, false);
+  } while (FileSystem::Instance().Exists(socket_path));
+
+  Args args(
+      {qemu, "-g", socket_path, launch_info.GetExecutableFile().GetPath()});
+  for (size_t i = 1; i < launch_info.GetArguments().size(); ++i)
+    args.AppendArgument(launch_info.GetArguments()[i].ref());
+
+  LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()),
+           get_arg_range(args));
+
+  launch_info.SetArguments(args, true);
+  launch_info.SetLaunchInSeparateProcessGroup(true);
+  launch_info.GetFlags().Clear(eLaunchFlagDebug);
+  launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback,
+                                        false);
+
+  error = Host::LaunchProcess(launch_info);
+  if (error.Fail())
+    return nullptr;
+
+  ProcessSP process_sp = target.CreateProcess(
+      launch_info.GetListener(),
+      process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr,
+      true);
+  ListenerSP listener_sp =
+      Listener::MakeListener("lldb.platform_qemu_user.debugprocess");
+  launch_info.SetHijackListener(listener_sp);
+  Process::ProcessEventHijacker hijacker(*process_sp, listener_sp);
+
+  error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str());
+  if (error.Fail())
+    return nullptr;
+
+  process_sp->WaitForProcessToStop(llvm::None, nullptr, false, listener_sp);
+  return process_sp;
+}
Index: lldb/source/Plugins/Platform/Qemu/PlatformQemuProperties.td
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Platform/Qemu/PlatformQemuProperties.td
@@ -0,0 +1,12 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "platformqemuuser" in {
+  def Architecture: Property<"architecture", "String">,
+    Global,
+    DefaultStringValue<"">,
+    Desc<"Architecture to emulate.">;
+  def EmulatorPath: Property<"emulator-path", "FileSpec">,
+    Global,
+    DefaultStringValue<"">,
+    Desc<"Path to the emulator binary.">;
+}
Index: lldb/source/Plugins/Platform/Qemu/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Platform/Qemu/CMakeLists.txt
@@ -0,0 +1,20 @@
+lldb_tablegen(PlatformQemuProperties.inc -gen-lldb-property-defs
+  SOURCE PlatformQemuProperties.td
+  TARGET LLDBPluginPlatformQemuPropertiesGen)
+
+lldb_tablegen(PlatformQemuPropertiesEnum.inc -gen-lldb-property-enum-defs
+  SOURCE PlatformQemuProperties.td
+  TARGET LLDBPluginPlatformQemuPropertiesEnumGen)
+
+add_lldb_library(lldbPluginPlatformQemu PLUGIN
+  PlatformQemuUser.cpp
+
+  LINK_LIBS
+    lldbUtility
+  LINK_COMPONENTS
+    Support
+    )
+
+add_dependencies(lldbPluginPlatformQemu
+  LLDBPluginPlatformQemuPropertiesGen
+  LLDBPluginPlatformQemuPropertiesEnumGen)
Index: lldb/source/Plugins/Platform/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Platform/CMakeLists.txt
+++ lldb/source/Plugins/Platform/CMakeLists.txt
@@ -6,4 +6,5 @@
 add_subdirectory(NetBSD)
 add_subdirectory(OpenBSD)
 add_subdirectory(POSIX)
+add_subdirectory(Qemu)
 add_subdirectory(Windows)
Index: lldb/packages/Python/lldbsuite/test/gdbclientutils.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/gdbclientutils.py
+++ lldb/packages/Python/lldbsuite/test/gdbclientutils.py
@@ -339,7 +339,7 @@
         pass
 
 
-class ServerSocket:
+class ServerChannel:
     """
     A wrapper class for TCP or pty-based server.
     """
@@ -366,22 +366,14 @@
         """Send the data to the connected client."""
 
 
-class TCPServerSocket(ServerSocket):
-    def __init__(self):
-        family, type, proto, _, addr = socket.getaddrinfo(
-                "localhost", 0, proto=socket.IPPROTO_TCP)[0]
+class ServerSocket(ServerChannel):
+    def __init__(self, family, type, proto, addr):
         self._server_socket = socket.socket(family, type, proto)
         self._connection = None
 
         self._server_socket.bind(addr)
         self._server_socket.listen(1)
 
-    def get_connect_address(self):
-        return "[{}]:{}".format(*self._server_socket.getsockname())
-
-    def get_connect_url(self):
-        return "connect://" + self.get_connect_address()
-
     def close_server(self):
         self._server_socket.close()
 
@@ -410,7 +402,31 @@
         return self._connection.sendall(data)
 
 
-class PtyServerSocket(ServerSocket):
+class TCPServerSocket(ServerSocket):
+    def __init__(self):
+        family, type, proto, _, addr = socket.getaddrinfo(
+                "localhost", 0, proto=socket.IPPROTO_TCP)[0]
+        super().__init__(family, type, proto, addr)
+
+    def get_connect_address(self):
+        return "[{}]:{}".format(*self._server_socket.getsockname())
+
+    def get_connect_url(self):
+        return "connect://" + self.get_connect_address()
+
+
+class UnixServerSocket(ServerSocket):
+    def __init__(self, addr):
+        super().__init__(socket.AF_UNIX, socket.SOCK_STREAM, 0, addr)
+
+    def get_connect_address(self):
+        return self._server_socket.getsockname()
+
+    def get_connect_url(self):
+        return "unix-connect://" + self.get_connect_address()
+
+
+class PtyServerSocket(ServerChannel):
     def __init__(self):
         import pty
         import tty
@@ -486,6 +502,7 @@
         try:
             self._socket.accept()
         except:
+            traceback.print_exc()
             return
         self._shouldSendAck = True
         self._receivedData = ""
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to