Skip to content

Commit f63bc84

Browse files
authored
[lldb] Fix FindProcessImpl() for iOS simulators (llvm#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.
1 parent 1979750 commit f63bc84

File tree

7 files changed

+179
-83
lines changed

7 files changed

+179
-83
lines changed

lldb/packages/Python/lldbsuite/test/lldbutil.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# System modules
88
import errno
99
import io
10+
import json
1011
import os
1112
import re
1213
import sys
@@ -1704,3 +1705,89 @@ def packetlog_get_dylib_info(log):
17041705
expect_dylib_info_response = True
17051706

17061707
return dylib_info
1708+
1709+
1710+
# ========================
1711+
# Utilities for simulators
1712+
# ========================
1713+
1714+
1715+
def get_latest_apple_simulator(platform_name, log=None):
1716+
# Run simctl to list all simulators
1717+
cmd = ["xcrun", "simctl", "list", "-j", "devices"]
1718+
cmd_str = " ".join(cmd)
1719+
if log:
1720+
log(cmd_str)
1721+
sim_devices_str = subprocess.check_output(cmd).decode("utf-8")
1722+
sim_devices = json.loads(sim_devices_str)["devices"]
1723+
1724+
# Find an available simulator for the requested platform
1725+
device_uuid = None
1726+
device_runtime = None
1727+
for simulator in sim_devices:
1728+
if isinstance(simulator, dict):
1729+
runtime = simulator["name"]
1730+
devices = simulator["devices"]
1731+
else:
1732+
runtime = simulator
1733+
devices = sim_devices[simulator]
1734+
if not platform_name in runtime.lower():
1735+
continue
1736+
for device in devices:
1737+
if "availability" in device and device["availability"] != "(available)":
1738+
continue
1739+
if "isAvailable" in device and not device["isAvailable"]:
1740+
continue
1741+
if device_runtime and runtime < device_runtime:
1742+
continue
1743+
device_uuid = device["udid"]
1744+
device_runtime = runtime
1745+
# Stop searching in this runtime
1746+
break
1747+
1748+
return device_uuid
1749+
1750+
1751+
def launch_exe_in_apple_simulator(
1752+
device_uuid,
1753+
exe_path,
1754+
exe_args=[],
1755+
stderr_lines_to_read=0,
1756+
stderr_patterns=[],
1757+
log=None,
1758+
):
1759+
exe_path = os.path.realpath(exe_path)
1760+
cmd = [
1761+
"xcrun",
1762+
"simctl",
1763+
"spawn",
1764+
"-s",
1765+
device_uuid,
1766+
exe_path,
1767+
] + exe_args
1768+
if log:
1769+
log(" ".join(cmd))
1770+
sim_launcher = subprocess.Popen(cmd, stderr=subprocess.PIPE)
1771+
1772+
# Read stderr to try to find matches.
1773+
# Each pattern will return the value of group[1] of the first match in the stderr.
1774+
# Will read at most stderr_lines_to_read lines.
1775+
# Will early terminate when all matches have been found.
1776+
total_patterns = len(stderr_patterns)
1777+
matches_found = 0
1778+
matched_strings = [None] * total_patterns
1779+
for _ in range(0, stderr_lines_to_read):
1780+
stderr = sim_launcher.stderr.readline().decode("utf-8")
1781+
if not stderr:
1782+
continue
1783+
for i, pattern in enumerate(stderr_patterns):
1784+
if matched_strings[i] is not None:
1785+
continue
1786+
match = re.match(pattern, stderr)
1787+
if match:
1788+
matched_strings[i] = str(match.group(1))
1789+
matches_found += 1
1790+
if matches_found == total_patterns:
1791+
break
1792+
1793+
return exe_path, matched_strings

lldb/source/Host/macosx/objcxx/Host.mm

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,9 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size,
595595
const llvm::Triple::ArchType triple_arch = triple.getArch();
596596
const bool check_for_ios_simulator =
597597
(triple_arch == llvm::Triple::x86 ||
598-
triple_arch == llvm::Triple::x86_64);
598+
triple_arch == llvm::Triple::x86_64 ||
599+
triple_arch == llvm::Triple::aarch64);
600+
599601
const char *cstr = data.GetCStr(&offset);
600602
if (cstr) {
601603
process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
@@ -621,21 +623,20 @@ DataExtractor data(arg_data.GetBytes(), arg_data_size,
621623
}
622624

623625
Environment &proc_env = process_info.GetEnvironment();
624-
while ((cstr = data.GetCStr(&offset))) {
625-
if (cstr[0] == '\0')
626-
break;
627-
628-
if (check_for_ios_simulator) {
629-
if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) ==
630-
0)
631-
process_info.GetArchitecture().GetTriple().setOS(
632-
llvm::Triple::IOS);
633-
else
634-
process_info.GetArchitecture().GetTriple().setOS(
635-
llvm::Triple::MacOSX);
636-
}
637-
638-
proc_env.insert(cstr);
626+
bool is_simulator = false;
627+
llvm::StringRef env_var;
628+
while (!(env_var = data.GetCStr(&offset)).empty()) {
629+
if (check_for_ios_simulator &&
630+
env_var.starts_with("SIMULATOR_UDID="))
631+
is_simulator = true;
632+
proc_env.insert(env_var);
633+
}
634+
llvm::Triple &triple = process_info.GetArchitecture().GetTriple();
635+
if (is_simulator) {
636+
triple.setOS(llvm::Triple::IOS);
637+
triple.setEnvironment(llvm::Triple::Simulator);
638+
} else {
639+
triple.setOS(llvm::Triple::MacOSX);
639640
}
640641
return true;
641642
}
@@ -741,8 +742,8 @@ static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) {
741742
!match_info.ProcessIDsMatch(process_info))
742743
continue;
743744

744-
// Get CPU type first so we can know to look for iOS simulator is we have
745-
// x86 or x86_64
745+
// Get CPU type first so we can know to look for iOS simulator if we have
746+
// a compatible type.
746747
if (GetMacOSXProcessCPUType(process_info)) {
747748
if (GetMacOSXProcessArgs(&match_info, process_info)) {
748749
if (match_info.Matches(process_info))
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
C_SOURCES := hello.c
1+
CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS
2+
ENABLE_THREADS := YES
3+
CXX_SOURCES := hello.cpp
4+
MAKE_DSYM := NO
25

36
include Makefile.rules

lldb/test/API/macosx/simulator/TestSimulatorPlatform.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def check_debugserver(self, log, expected_platform, expected_version):
3939
if expected_version:
4040
self.assertEqual(aout_info["min_version_os_sdk"], expected_version)
4141

42-
def run_with(self, arch, os, vers, env, expected_load_command):
42+
def run_with(
43+
self, arch, os, vers, env, expected_load_command, expected_platform=None
44+
):
4345
env_list = [env] if env else []
4446
triple = "-".join([arch, "apple", os + vers] + env_list)
4547
sdk = lldbutil.get_xcode_sdk(os, env)
@@ -72,12 +74,47 @@ def run_with(self, arch, os, vers, env, expected_load_command):
7274
log = self.getBuildArtifact("packets.log")
7375
self.expect("log enable gdb-remote packets -f " + log)
7476
lldbutil.run_to_source_breakpoint(
75-
self, "break here", lldb.SBFileSpec("hello.c")
77+
self, "break here", lldb.SBFileSpec("hello.cpp")
7678
)
7779
triple_re = "-".join([arch, "apple", os + vers + ".*"] + env_list)
7880
self.expect("image list -b -t", patterns=[r"a\.out " + triple_re])
7981
self.check_debugserver(log, os + env, vers)
8082

83+
if expected_platform is not None:
84+
# Verify the platform name.
85+
self.expect(
86+
"platform status",
87+
patterns=[r"Platform: " + expected_platform + "-simulator"],
88+
)
89+
90+
# Launch exe in simulator and verify that `platform process list` can find the process.
91+
# This separate launch is needed because the command ignores processes which are being debugged.
92+
device_udid = lldbutil.get_latest_apple_simulator(
93+
expected_platform, self.trace
94+
)
95+
_, matched_strings = lldbutil.launch_exe_in_apple_simulator(
96+
device_udid,
97+
self.getBuildArtifact("a.out"),
98+
exe_args=[],
99+
stderr_lines_to_read=1, # in hello.cpp, the pid is printed first
100+
stderr_patterns=[r"PID: (.*)"],
101+
log=self.trace,
102+
)
103+
104+
# Make sure we found the PID.
105+
self.assertIsNotNone(matched_strings[0])
106+
pid = int(matched_strings[0])
107+
108+
# Verify that processes on the platform can be listed.
109+
self.expect(
110+
"platform process list",
111+
patterns=[
112+
r"\d+ matching processes were found on \"%s-simulator\""
113+
% expected_platform,
114+
r"%d .+ a.out" % pid,
115+
],
116+
)
117+
81118
@skipIfAsan
82119
@skipUnlessDarwin
83120
@skipIfDarwinEmbedded
@@ -90,6 +127,7 @@ def test_ios(self):
90127
vers="",
91128
env="simulator",
92129
expected_load_command="LC_BUILD_VERSION",
130+
expected_platform="ios",
93131
)
94132

95133
@skipIfAsan

lldb/test/API/macosx/simulator/hello.c

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <stdio.h>
2+
#include <thread>
3+
#include <unistd.h>
4+
5+
static void print_pid() { fprintf(stderr, "PID: %d\n", getpid()); }
6+
7+
static void sleep() { std::this_thread::sleep_for(std::chrono::seconds(10)); }
8+
9+
int main(int argc, char **argv) {
10+
print_pid();
11+
puts("break here\n");
12+
sleep();
13+
return 0;
14+
}

lldb/test/API/tools/lldb-server/TestAppleSimulatorOSType.py

Lines changed: 15 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,12 @@ class TestAppleSimulatorOSType(gdbremote_testcase.GdbRemoteTestCaseBase):
1414
READ_LINES = 10
1515

1616
def check_simulator_ostype(self, sdk, platform_name, arch=platform.machine()):
17-
cmd = ["xcrun", "simctl", "list", "-j", "devices"]
18-
cmd_str = " ".join(cmd)
19-
self.trace(cmd_str)
20-
sim_devices_str = subprocess.check_output(cmd).decode("utf-8")
17+
# Get simulator
18+
deviceUDID = None
2119
try:
22-
sim_devices = json.loads(sim_devices_str)["devices"]
20+
deviceUDID = lldbutil.get_latest_apple_simulator(platform_name, self.trace)
2321
except json.decoder.JSONDecodeError:
24-
self.fail(
25-
"Could not parse '{}' output. Authorization denied?".format(cmd_str)
26-
)
27-
# Find an available simulator for the requested platform
28-
deviceUDID = None
29-
deviceRuntime = None
30-
for simulator in sim_devices:
31-
if isinstance(simulator, dict):
32-
runtime = simulator["name"]
33-
devices = simulator["devices"]
34-
else:
35-
runtime = simulator
36-
devices = sim_devices[simulator]
37-
if not platform_name in runtime.lower():
38-
continue
39-
for device in devices:
40-
if "availability" in device and device["availability"] != "(available)":
41-
continue
42-
if "isAvailable" in device and not device["isAvailable"]:
43-
continue
44-
if deviceRuntime and runtime < deviceRuntime:
45-
continue
46-
deviceUDID = device["udid"]
47-
deviceRuntime = runtime
48-
# Stop searching in this runtime
49-
break
50-
22+
self.fail("Could not parse output. Authorization denied?")
5123
if not deviceUDID:
5224
self.skipTest(
5325
"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()):
7850
},
7951
compiler=clang,
8052
)
81-
exe_path = os.path.realpath(self.getBuildArtifact(exe_name))
82-
cmd = [
83-
"xcrun",
84-
"simctl",
85-
"spawn",
86-
"-s",
53+
54+
# Launch the executable in the simulator
55+
exe_path, matched_groups = lldbutil.launch_exe_in_apple_simulator(
8756
deviceUDID,
88-
exe_path,
89-
"print-pid",
90-
"sleep:10",
91-
]
92-
self.trace(" ".join(cmd))
93-
sim_launcher = subprocess.Popen(cmd, stderr=subprocess.PIPE)
94-
# Get the PID from the process output
95-
pid = None
96-
97-
# Read the first READ_LINES to try to find the PID.
98-
for _ in range(0, self.READ_LINES):
99-
stderr = sim_launcher.stderr.readline().decode("utf-8")
100-
if not stderr:
101-
continue
102-
match = re.match(r"PID: (.*)", stderr)
103-
if match:
104-
pid = int(match.group(1))
105-
break
57+
self.getBuildArtifact(exe_name),
58+
["print-pid", "sleep:10"],
59+
self.READ_LINES,
60+
[r"PID: (.*)"],
61+
self.trace,
62+
)
10663

10764
# Make sure we found the PID.
108-
self.assertIsNotNone(pid)
65+
self.assertIsNotNone(matched_groups[0])
66+
pid = int(matched_groups[0])
10967

11068
# Launch debug monitor attaching to the simulated process
11169
server = self.connect_to_debug_monitor(attach_pid=pid)

0 commit comments

Comments
 (0)