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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits