83 changes: 83 additions & 0 deletions lldb/test/API/qemu/TestQemuLaunch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from __future__ import print_function
import lldb
import unittest
import os
import json
import stat
import sys
from textwrap import dedent
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 set_emulator_setting(self, name, value):
self.runCmd("settings set platform.plugin.qemu-user.%s %s" %
(name, value))

def setUp(self):
super().setUp()
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(dedent("""\
#! {exec!s}
import runpy
import sys
sys.path = {path!r}
runpy.run_path({source!r}, run_name='__main__')
""").format(exec=sys.executable, path=sys.path,
source=self.getSourcePath("qemu.py")))

self.set_emulator_setting("architecture", self.getArchitecture())
self.set_emulator_setting("emulator-path", emulator)

def test_basic_launch(self):
self.build()
exe = self.getBuildArtifact()

# Create a target using out platform
error = lldb.SBError()
target = self.dbg.CreateTarget(exe, '', 'qemu-user', False, error)
self.assertSuccess(error)
self.assertEqual(target.GetPlatform().GetName(), "qemu-user")

# "Launch" the process. Our fake qemu implementation will pretend it
# immediately exited.
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)

# Verify the qemu invocation parameters.
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.set_emulator_setting("emulator-path",
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())
3 changes: 3 additions & 0 deletions lldb/test/API/qemu/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NB: This code will never be run, but we do need a realistic-looking
// executable for the tests.
int main() {}
37 changes: 37 additions & 0 deletions lldb/test/API/qemu/qemu.py
Original file line number Diff line number Diff line change
@@ -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()