[lldb] Fix FindProcessImpl() for iOS simulators (#139174)
# Benefit This patch fixes: 1. After `platform select ios-simulator`, `platform process list` will now print processes which are running in the iOS simulator. Previously, no process will be listed. 2. After `platform select ios-simulator`, `platform attach --name <name>` will succeed. Previously, it will error out saying no process is found. # Several bugs that is being fixed 1. During the process listing, add `aarch64` to the list of CPU types for which iOS simulators are checked for. 2. Given a candidate process, when checking for simulators, the original code will find the desired environment variable (`SIMULATOR_UDID`) and set the OS to iOS, but then the immediate next environment variable will set it back to macOS. 3. For processes running on simulator, set the triple's `Environment` to `Simulator`, so that such processes can pass the filtering [in this line](https://fburl.com/8nivnrjx). The original code leave it as the default `UnknownEnvironment`. # Manual test **With this patch:** ``` royshi-mac-home ~/public_llvm/build % bin/lldb (lldb) platform select ios-simulator (lldb) platform process list 240 matching processes were found on "ios-simulator" PID PARENT USER TRIPLE NAME ====== ====== ========== ============================== ============================ 40511 28844 royshi arm64-apple-ios-simulator FocusPlayground // my toy iOS app running on simulator ... // omit 28844 1 royshi arm64-apple-ios-simulator launchd_sim (lldb) process attach --name FocusPlayground Process 40511 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP frame #0: 0x0000000104e3cb70 libsystem_kernel.dylib`mach_msg2_trap + 8 libsystem_kernel.dylib`mach_msg2_trap: -> 0x104e3cb70 <+8>: ret ... // omit ``` **Without this patch:** ``` $ bin/lldb (lldb) platform select ios-simulator (lldb) platform process list error: no processes were found on the "ios-simulator" platform (lldb) process attach --name FocusPlayground error: attach failed: could not find a process named FocusPlayground ``` # Unittest See PR.
This commit is contained in:
@@ -7,6 +7,7 @@ They can also be useful for general purpose lldb scripting.
|
||||
# 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
|
||||
|
||||
@@ -595,7 +595,9 @@ static bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
|
||||
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 @@ static bool GetMacOSXProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
proc_env.insert(cstr);
|
||||
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 @@ uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,7 +39,9 @@ class TestSimulatorPlatformLaunching(TestBase):
|
||||
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 @@ class TestSimulatorPlatformLaunching(TestBase):
|
||||
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 @@ class TestSimulatorPlatformLaunching(TestBase):
|
||||
vers="",
|
||||
env="simulator",
|
||||
expected_load_command="LC_BUILD_VERSION",
|
||||
expected_platform="ios",
|
||||
)
|
||||
|
||||
@skipIfAsan
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
void puts(char *);
|
||||
int main(int argc, char **argv) {
|
||||
puts("break here\n");
|
||||
return 0;
|
||||
}
|
||||
14
lldb/test/API/macosx/simulator/hello.cpp
Normal file
14
lldb/test/API/macosx/simulator/hello.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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")
|
||||
try:
|
||||
sim_devices = json.loads(sim_devices_str)["devices"]
|
||||
except json.decoder.JSONDecodeError:
|
||||
self.fail(
|
||||
"Could not parse '{}' output. Authorization denied?".format(cmd_str)
|
||||
)
|
||||
# Find an available simulator for the requested platform
|
||||
# Get simulator
|
||||
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
|
||||
|
||||
try:
|
||||
deviceUDID = lldbutil.get_latest_apple_simulator(platform_name, self.trace)
|
||||
except json.decoder.JSONDecodeError:
|
||||
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 @@ class TestAppleSimulatorOSType(gdbremote_testcase.GdbRemoteTestCaseBase):
|
||||
},
|
||||
compiler=clang,
|
||||
)
|
||||
exe_path = os.path.realpath(self.getBuildArtifact(exe_name))
|
||||
cmd = [
|
||||
"xcrun",
|
||||
"simctl",
|
||||
"spawn",
|
||||
"-s",
|
||||
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
|
||||
# Launch the executable in the simulator
|
||||
exe_path, matched_groups = lldbutil.launch_exe_in_apple_simulator(
|
||||
deviceUDID,
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user