diff --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py index 27e0040034370..8112705438c1f 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbutil.py +++ b/lldb/packages/Python/lldbsuite/test/lldbutil.py @@ -7,6 +7,7 @@ # System modules import errno import io +import json import os import re import sys @@ -1704,3 +1705,89 @@ def packetlog_get_dylib_info(log): expect_dylib_info_response = True return dylib_info + + +# ======================== +# Utilities for simulators +# ======================== + + +def get_latest_apple_simulator(platform_name, log=None): + # Run simctl to list all simulators + cmd = ["xcrun", "simctl", "list", "-j", "devices"] + cmd_str = " ".join(cmd) + if log: + log(cmd_str) + sim_devices_str = subprocess.check_output(cmd).decode("utf-8") + sim_devices = json.loads(sim_devices_str)["devices"] + + # Find an available simulator for the requested platform + device_uuid = None + device_runtime = None + for simulator in sim_devices: + if isinstance(simulator, dict): + runtime = simulator["name"] + devices = simulator["devices"] + else: + runtime = simulator + devices = sim_devices[simulator] + if not platform_name in runtime.lower(): + continue + for device in devices: + if "availability" in device and device["availability"] != "(available)": + continue + if "isAvailable" in device and not device["isAvailable"]: + continue + if device_runtime and runtime < device_runtime: + continue + device_uuid = device["udid"] + device_runtime = runtime + # Stop searching in this runtime + break + + return device_uuid + + +def launch_exe_in_apple_simulator( + device_uuid, + exe_path, + exe_args=[], + stderr_lines_to_read=0, + stderr_patterns=[], + log=None, +): + exe_path = os.path.realpath(exe_path) + cmd = [ + "xcrun", + "simctl", + "spawn", + "-s", + device_uuid, + exe_path, + ] + exe_args + if log: + log(" ".join(cmd)) + sim_launcher = subprocess.Popen(cmd, stderr=subprocess.PIPE) + + # Read stderr to try to find matches. + # Each pattern will return the value of group[1] of the first match in the stderr. + # Will read at most stderr_lines_to_read lines. + # Will early terminate when all matches have been found. + total_patterns = len(stderr_patterns) + matches_found = 0 + matched_strings = [None] * total_patterns + for _ in range(0, stderr_lines_to_read): + stderr = sim_launcher.stderr.readline().decode("utf-8") + if not stderr: + continue + for i, pattern in enumerate(stderr_patterns): + if matched_strings[i] is not None: + continue + match = re.match(pattern, stderr) + if match: + matched_strings[i] = str(match.group(1)) + matches_found += 1 + if matches_found == total_patterns: + break + + return exe_path, matched_strings diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index e187bf98188ae..3c1d1179963d2 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -595,7 +595,9 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size, const llvm::Triple::ArchType triple_arch = triple.getArch(); const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || - triple_arch == llvm::Triple::x86_64); + triple_arch == llvm::Triple::x86_64 || + triple_arch == llvm::Triple::aarch64); + const char *cstr = data.GetCStr(&offset); if (cstr) { process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native); @@ -621,21 +623,20 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size, } Environment &proc_env = process_info.GetEnvironment(); - while ((cstr = data.GetCStr(&offset))) { - if (cstr[0] == '\0') - break; - - if (check_for_ios_simulator) { - if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == - 0) - process_info.GetArchitecture().GetTriple().setOS( - llvm::Triple::IOS); - else - process_info.GetArchitecture().GetTriple().setOS( - llvm::Triple::MacOSX); - } - - proc_env.insert(cstr); + bool is_simulator = false; + llvm::StringRef env_var; + while (!(env_var = data.GetCStr(&offset)).empty()) { + if (check_for_ios_simulator && + env_var.starts_with("SIMULATOR_UDID=")) + is_simulator = true; + proc_env.insert(env_var); + } + llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); + if (is_simulator) { + triple.setOS(llvm::Triple::IOS); + triple.setEnvironment(llvm::Triple::Simulator); + } else { + triple.setOS(llvm::Triple::MacOSX); } return true; } @@ -741,8 +742,8 @@ static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) { !match_info.ProcessIDsMatch(process_info)) continue; - // Get CPU type first so we can know to look for iOS simulator is we have - // x86 or x86_64 + // Get CPU type first so we can know to look for iOS simulator if we have + // a compatible type. if (GetMacOSXProcessCPUType(process_info)) { if (GetMacOSXProcessArgs(&match_info, process_info)) { if (match_info.Matches(process_info)) diff --git a/lldb/test/API/macosx/simulator/Makefile b/lldb/test/API/macosx/simulator/Makefile index 421e85117613a..ce3490d28ee8e 100644 --- a/lldb/test/API/macosx/simulator/Makefile +++ b/lldb/test/API/macosx/simulator/Makefile @@ -1,3 +1,6 @@ -C_SOURCES := hello.c +CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS +ENABLE_THREADS := YES +CXX_SOURCES := hello.cpp +MAKE_DSYM := NO include Makefile.rules diff --git a/lldb/test/API/macosx/simulator/TestSimulatorPlatform.py b/lldb/test/API/macosx/simulator/TestSimulatorPlatform.py index 74ba0ee6c83bb..b17ee83ea04fe 100644 --- a/lldb/test/API/macosx/simulator/TestSimulatorPlatform.py +++ b/lldb/test/API/macosx/simulator/TestSimulatorPlatform.py @@ -39,7 +39,9 @@ def check_debugserver(self, log, expected_platform, expected_version): if expected_version: self.assertEqual(aout_info["min_version_os_sdk"], expected_version) - def run_with(self, arch, os, vers, env, expected_load_command): + def run_with( + self, arch, os, vers, env, expected_load_command, expected_platform=None + ): env_list = [env] if env else [] triple = "-".join([arch, "apple", os + vers] + env_list) sdk = lldbutil.get_xcode_sdk(os, env) @@ -72,12 +74,47 @@ def run_with(self, arch, os, vers, env, expected_load_command): log = self.getBuildArtifact("packets.log") self.expect("log enable gdb-remote packets -f " + log) lldbutil.run_to_source_breakpoint( - self, "break here", lldb.SBFileSpec("hello.c") + self, "break here", lldb.SBFileSpec("hello.cpp") ) triple_re = "-".join([arch, "apple", os + vers + ".*"] + env_list) self.expect("image list -b -t", patterns=[r"a\.out " + triple_re]) self.check_debugserver(log, os + env, vers) + if expected_platform is not None: + # Verify the platform name. + self.expect( + "platform status", + patterns=[r"Platform: " + expected_platform + "-simulator"], + ) + + # Launch exe in simulator and verify that `platform process list` can find the process. + # This separate launch is needed because the command ignores processes which are being debugged. + device_udid = lldbutil.get_latest_apple_simulator( + expected_platform, self.trace + ) + _, matched_strings = lldbutil.launch_exe_in_apple_simulator( + device_udid, + self.getBuildArtifact("a.out"), + exe_args=[], + stderr_lines_to_read=1, # in hello.cpp, the pid is printed first + stderr_patterns=[r"PID: (.*)"], + log=self.trace, + ) + + # Make sure we found the PID. + self.assertIsNotNone(matched_strings[0]) + pid = int(matched_strings[0]) + + # Verify that processes on the platform can be listed. + self.expect( + "platform process list", + patterns=[ + r"\d+ matching processes were found on \"%s-simulator\"" + % expected_platform, + r"%d .+ a.out" % pid, + ], + ) + @skipIfAsan @skipUnlessDarwin @skipIfDarwinEmbedded @@ -90,6 +127,7 @@ def test_ios(self): vers="", env="simulator", expected_load_command="LC_BUILD_VERSION", + expected_platform="ios", ) @skipIfAsan diff --git a/lldb/test/API/macosx/simulator/hello.c b/lldb/test/API/macosx/simulator/hello.c deleted file mode 100644 index 9010d0e295154..0000000000000 --- a/lldb/test/API/macosx/simulator/hello.c +++ /dev/null @@ -1,5 +0,0 @@ -void puts(char *); -int main(int argc, char **argv) { - puts("break here\n"); - return 0; -} diff --git a/lldb/test/API/macosx/simulator/hello.cpp b/lldb/test/API/macosx/simulator/hello.cpp new file mode 100644 index 0000000000000..e202125da0929 --- /dev/null +++ b/lldb/test/API/macosx/simulator/hello.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +static void print_pid() { fprintf(stderr, "PID: %d\n", getpid()); } + +static void sleep() { std::this_thread::sleep_for(std::chrono::seconds(10)); } + +int main(int argc, char **argv) { + print_pid(); + puts("break here\n"); + sleep(); + return 0; +} diff --git a/lldb/test/API/tools/lldb-server/TestAppleSimulatorOSType.py b/lldb/test/API/tools/lldb-server/TestAppleSimulatorOSType.py index ed47f94e9492b..4fee27823f1fa 100644 --- a/lldb/test/API/tools/lldb-server/TestAppleSimulatorOSType.py +++ b/lldb/test/API/tools/lldb-server/TestAppleSimulatorOSType.py @@ -14,40 +14,12 @@ class TestAppleSimulatorOSType(gdbremote_testcase.GdbRemoteTestCaseBase): READ_LINES = 10 def check_simulator_ostype(self, sdk, platform_name, arch=platform.machine()): - cmd = ["xcrun", "simctl", "list", "-j", "devices"] - cmd_str = " ".join(cmd) - self.trace(cmd_str) - sim_devices_str = subprocess.check_output(cmd).decode("utf-8") + # Get simulator + deviceUDID = None try: - sim_devices = json.loads(sim_devices_str)["devices"] + deviceUDID = lldbutil.get_latest_apple_simulator(platform_name, self.trace) except json.decoder.JSONDecodeError: - self.fail( - "Could not parse '{}' output. Authorization denied?".format(cmd_str) - ) - # Find an available simulator for the requested platform - deviceUDID = None - deviceRuntime = None - for simulator in sim_devices: - if isinstance(simulator, dict): - runtime = simulator["name"] - devices = simulator["devices"] - else: - runtime = simulator - devices = sim_devices[simulator] - if not platform_name in runtime.lower(): - continue - for device in devices: - if "availability" in device and device["availability"] != "(available)": - continue - if "isAvailable" in device and not device["isAvailable"]: - continue - if deviceRuntime and runtime < deviceRuntime: - continue - deviceUDID = device["udid"] - deviceRuntime = runtime - # Stop searching in this runtime - break - + self.fail("Could not parse output. Authorization denied?") if not deviceUDID: self.skipTest( "Could not find a simulator for {} ({})".format(platform_name, arch) @@ -78,34 +50,20 @@ def check_simulator_ostype(self, sdk, platform_name, arch=platform.machine()): }, compiler=clang, ) - exe_path = os.path.realpath(self.getBuildArtifact(exe_name)) - cmd = [ - "xcrun", - "simctl", - "spawn", - "-s", + + # Launch the executable in the simulator + exe_path, matched_groups = lldbutil.launch_exe_in_apple_simulator( deviceUDID, - exe_path, - "print-pid", - "sleep:10", - ] - self.trace(" ".join(cmd)) - sim_launcher = subprocess.Popen(cmd, stderr=subprocess.PIPE) - # Get the PID from the process output - pid = None - - # Read the first READ_LINES to try to find the PID. - for _ in range(0, self.READ_LINES): - stderr = sim_launcher.stderr.readline().decode("utf-8") - if not stderr: - continue - match = re.match(r"PID: (.*)", stderr) - if match: - pid = int(match.group(1)) - break + self.getBuildArtifact(exe_name), + ["print-pid", "sleep:10"], + self.READ_LINES, + [r"PID: (.*)"], + self.trace, + ) # Make sure we found the PID. - self.assertIsNotNone(pid) + self.assertIsNotNone(matched_groups[0]) + pid = int(matched_groups[0]) # Launch debug monitor attaching to the simulated process server = self.connect_to_debug_monitor(attach_pid=pid)