1 //===-- PlatformAppleSimulator.cpp ----------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "PlatformAppleSimulator.h"
11 #if defined(__APPLE__)
15 #include "lldb/Core/Debugger.h"
16 #include "lldb/Core/Module.h"
17 #include "lldb/Core/PluginManager.h"
18 #include "lldb/Host/HostInfo.h"
19 #include "lldb/Host/PseudoTerminal.h"
20 #include "lldb/Target/Process.h"
21 #include "lldb/Utility/LLDBAssert.h"
22 #include "lldb/Utility/LLDBLog.h"
23 #include "lldb/Utility/Log.h"
24 #include "lldb/Utility/Status.h"
25 #include "lldb/Utility/StreamString.h"
27 #include "llvm/Support/Threading.h"
33 using namespace lldb_private
;
35 #if !defined(__APPLE__)
36 #define UNSUPPORTED_ERROR ("Apple simulators aren't supported on this platform")
39 /// Default Constructor
40 PlatformAppleSimulator::PlatformAppleSimulator(
41 const char *class_name
, const char *description
, ConstString plugin_name
,
42 llvm::Triple::OSType preferred_os
,
43 llvm::SmallVector
<llvm::StringRef
, 4> supported_triples
,
44 std::string sdk_name_primary
, std::string sdk_name_secondary
,
45 lldb_private::XcodeSDK::Type sdk_type
,
46 CoreSimulatorSupport::DeviceType::ProductFamilyID kind
)
47 : PlatformDarwin(true), m_class_name(class_name
),
48 m_description(description
), m_plugin_name(plugin_name
), m_kind(kind
),
49 m_os_type(preferred_os
), m_supported_triples(supported_triples
),
50 m_sdk_name_primary(std::move(sdk_name_primary
)),
51 m_sdk_name_secondary(std::move(sdk_name_secondary
)),
52 m_sdk_type(sdk_type
) {}
56 /// The destructor is virtual since this class is designed to be
57 /// inherited from by the plug-in instance.
58 PlatformAppleSimulator::~PlatformAppleSimulator() = default;
60 lldb_private::Status
PlatformAppleSimulator::LaunchProcess(
61 lldb_private::ProcessLaunchInfo
&launch_info
) {
62 #if defined(__APPLE__)
64 CoreSimulatorSupport::Device
device(GetSimulatorDevice());
66 if (device
.GetState() != CoreSimulatorSupport::Device::State::Booted
) {
68 device
.Boot(boot_err
);
73 auto spawned
= device
.Spawn(launch_info
);
76 launch_info
.SetProcessID(spawned
.GetPID());
79 return spawned
.GetError();
82 err
= Status::FromErrorString(UNSUPPORTED_ERROR
);
87 void PlatformAppleSimulator::GetStatus(Stream
&strm
) {
88 Platform::GetStatus(strm
);
89 llvm::StringRef sdk
= GetSDKFilepath();
91 strm
<< " SDK Path: \"" << sdk
<< "\"\n";
93 strm
<< " SDK Path: error: unable to locate SDK\n";
95 #if defined(__APPLE__)
96 // This will get called by subclasses, so just output status on the current
98 PlatformAppleSimulator::LoadCoreSimulator();
100 std::string developer_dir
= HostInfo::GetXcodeDeveloperDirectory().GetPath();
101 CoreSimulatorSupport::DeviceSet devices
=
102 CoreSimulatorSupport::DeviceSet::GetAvailableDevices(
103 developer_dir
.c_str());
104 const size_t num_devices
= devices
.GetNumDevices();
106 strm
.Printf("Available devices:\n");
107 for (size_t i
= 0; i
< num_devices
; ++i
) {
108 CoreSimulatorSupport::Device device
= devices
.GetDeviceAtIndex(i
);
109 strm
<< " " << device
.GetUDID() << ": " << device
.GetName() << "\n";
112 if (m_device
.has_value() && m_device
->operator bool()) {
113 strm
<< "Current device: " << m_device
->GetUDID() << ": "
114 << m_device
->GetName();
115 if (m_device
->GetState() == CoreSimulatorSupport::Device::State::Booted
) {
116 strm
<< " state = booted";
118 strm
<< "\nType \"platform connect <ARG>\" where <ARG> is a device "
119 "UDID or a device name to disconnect and connect to a "
120 "different device.\n";
123 strm
<< "No current device is selected, \"platform connect <ARG>\" "
124 "where <ARG> is a device UDID or a device name to connect to "
125 "a specific device.\n";
129 strm
<< "No devices are available.\n";
132 strm
<< UNSUPPORTED_ERROR
;
136 Status
PlatformAppleSimulator::ConnectRemote(Args
&args
) {
137 #if defined(__APPLE__)
139 if (args
.GetArgumentCount() == 1) {
142 PlatformAppleSimulator::LoadCoreSimulator();
143 const char *arg_cstr
= args
.GetArgumentAtIndex(0);
145 std::string
arg_str(arg_cstr
);
146 std::string developer_dir
= HostInfo::GetXcodeDeveloperDirectory().GetPath();
147 CoreSimulatorSupport::DeviceSet devices
=
148 CoreSimulatorSupport::DeviceSet::GetAvailableDevices(
149 developer_dir
.c_str());
151 [this, &arg_str
](const CoreSimulatorSupport::Device
&device
) -> bool {
152 if (arg_str
== device
.GetUDID() || arg_str
== device
.GetName()) {
154 return false; // Stop iterating
156 return true; // Keep iterating
160 error
= Status::FromErrorStringWithFormat(
161 "no device with UDID or name '%s' was found", arg_cstr
);
164 error
= Status::FromErrorString(
165 "this command take a single UDID argument of the "
166 "device you want to connect to.");
171 err
= Status::FromErrorString(UNSUPPORTED_ERROR
);
176 Status
PlatformAppleSimulator::DisconnectRemote() {
177 #if defined(__APPLE__)
182 err
= Status::FromErrorString(UNSUPPORTED_ERROR
);
188 PlatformAppleSimulator::DebugProcess(ProcessLaunchInfo
&launch_info
,
189 Debugger
&debugger
, Target
&target
,
191 #if defined(__APPLE__)
192 ProcessSP process_sp
;
193 // Make sure we stop at the entry point
194 launch_info
.GetFlags().Set(eLaunchFlagDebug
);
195 // We always launch the process we are going to debug in a separate process
196 // group, since then we can handle ^C interrupts ourselves w/o having to
197 // worry about the target getting them as well.
198 launch_info
.SetLaunchInSeparateProcessGroup(true);
200 error
= LaunchProcess(launch_info
);
201 if (error
.Success()) {
202 if (launch_info
.GetProcessID() != LLDB_INVALID_PROCESS_ID
) {
203 ProcessAttachInfo
attach_info(launch_info
);
204 process_sp
= Attach(attach_info
, debugger
, &target
, error
);
206 launch_info
.SetHijackListener(attach_info
.GetHijackListener());
208 // Since we attached to the process, it will think it needs to detach
209 // if the process object just goes away without an explicit call to
210 // Process::Kill() or Process::Detach(), so let it know to kill the
211 // process if this happens.
212 process_sp
->SetShouldDetach(false);
214 // If we didn't have any file actions, the pseudo terminal might have
215 // been used where the secondary side was given as the file to open for
216 // stdin/out/err after we have already opened the primary so we can
217 // read/write stdin/out/err.
218 int pty_fd
= launch_info
.GetPTY().ReleasePrimaryFileDescriptor();
219 if (pty_fd
!= PseudoTerminal::invalid_fd
) {
220 process_sp
->SetSTDIOFileDescriptor(pty_fd
);
232 FileSpec
PlatformAppleSimulator::GetCoreSimulatorPath() {
233 #if defined(__APPLE__)
234 std::lock_guard
<std::mutex
> guard(m_core_sim_path_mutex
);
235 if (!m_core_simulator_framework_path
.has_value()) {
236 m_core_simulator_framework_path
=
237 FileSpec("/Library/Developer/PrivateFrameworks/CoreSimulator.framework/"
239 FileSystem::Instance().Resolve(*m_core_simulator_framework_path
);
241 return m_core_simulator_framework_path
.value();
247 void PlatformAppleSimulator::LoadCoreSimulator() {
248 #if defined(__APPLE__)
249 static llvm::once_flag g_load_core_sim_flag
;
250 llvm::call_once(g_load_core_sim_flag
, [this] {
251 const std::string
core_sim_path(GetCoreSimulatorPath().GetPath());
252 if (core_sim_path
.size())
253 dlopen(core_sim_path
.c_str(), RTLD_LAZY
);
258 #if defined(__APPLE__)
259 CoreSimulatorSupport::Device
PlatformAppleSimulator::GetSimulatorDevice() {
260 if (!m_device
.has_value()) {
261 const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id
= m_kind
;
262 std::string developer_dir
=
263 HostInfo::GetXcodeDeveloperDirectory().GetPath();
264 m_device
= CoreSimulatorSupport::DeviceSet::GetAvailableDevices(
265 developer_dir
.c_str())
266 .GetFanciest(dev_id
);
269 if (m_device
.has_value())
270 return m_device
.value();
272 return CoreSimulatorSupport::Device();
276 std::vector
<ArchSpec
> PlatformAppleSimulator::GetSupportedArchitectures(
277 const ArchSpec
&process_host_arch
) {
278 std::vector
<ArchSpec
> result(m_supported_triples
.size());
279 llvm::transform(m_supported_triples
, result
.begin(),
280 [](llvm::StringRef triple
) { return ArchSpec(triple
); });
284 static llvm::StringRef
GetXcodeSDKDir(std::string preferred
,
285 std::string secondary
) {
287 auto get_sdk
= [&](std::string sdk
) -> llvm::StringRef
{
288 auto sdk_path_or_err
=
289 HostInfo::GetSDKRoot(HostInfo::SDKOptions
{XcodeSDK(std::move(sdk
))});
290 if (!sdk_path_or_err
) {
291 Debugger::ReportError("Error while searching for Xcode SDK: " +
292 toString(sdk_path_or_err
.takeError()));
295 return *sdk_path_or_err
;
298 sdk
= get_sdk(preferred
);
300 sdk
= get_sdk(secondary
);
304 llvm::StringRef
PlatformAppleSimulator::GetSDKFilepath() {
305 if (!m_have_searched_for_sdk
) {
306 m_sdk
= GetXcodeSDKDir(m_sdk_name_primary
, m_sdk_name_secondary
);
307 m_have_searched_for_sdk
= true;
312 PlatformSP
PlatformAppleSimulator::CreateInstance(
313 const char *class_name
, const char *description
, ConstString plugin_name
,
314 llvm::SmallVector
<llvm::Triple::ArchType
, 4> supported_arch
,
315 llvm::Triple::OSType preferred_os
,
316 llvm::SmallVector
<llvm::Triple::OSType
, 4> supported_os
,
317 llvm::SmallVector
<llvm::StringRef
, 4> supported_triples
,
318 std::string sdk_name_primary
, std::string sdk_name_secondary
,
319 lldb_private::XcodeSDK::Type sdk_type
,
320 CoreSimulatorSupport::DeviceType::ProductFamilyID kind
, bool force
,
321 const ArchSpec
*arch
) {
322 Log
*log
= GetLog(LLDBLog::Platform
);
324 const char *arch_name
;
325 if (arch
&& arch
->GetArchitectureName())
326 arch_name
= arch
->GetArchitectureName();
328 arch_name
= "<null>";
330 const char *triple_cstr
=
331 arch
? arch
->GetTriple().getTriple().c_str() : "<null>";
333 LLDB_LOGF(log
, "%s::%s(force=%s, arch={%s,%s})", class_name
, __FUNCTION__
,
334 force
? "true" : "false", arch_name
, triple_cstr
);
338 if (!create
&& arch
&& arch
->IsValid()) {
339 if (llvm::is_contained(supported_arch
, arch
->GetMachine())) {
340 const llvm::Triple
&triple
= arch
->GetTriple();
341 switch (triple
.getVendor()) {
342 case llvm::Triple::Apple
:
346 #if defined(__APPLE__)
347 // Only accept "unknown" for the vendor if the host is Apple and if
348 // "unknown" wasn't specified (it was just returned because it was NOT
350 case llvm::Triple::UnknownVendor
:
351 create
= !arch
->TripleVendorWasSpecified();
359 if (llvm::is_contained(supported_os
, triple
.getOS()))
361 #if defined(__APPLE__)
362 // Only accept "unknown" for the OS if the host is Apple and it
363 // "unknown" wasn't specified (it was just returned because it was NOT
365 else if (triple
.getOS() == llvm::Triple::UnknownOS
)
366 create
= !arch
->TripleOSWasSpecified();
374 LLDB_LOGF(log
, "%s::%s() creating platform", class_name
, __FUNCTION__
);
376 return PlatformSP(new PlatformAppleSimulator(
377 class_name
, description
, plugin_name
, preferred_os
, supported_triples
,
378 sdk_name_primary
, sdk_name_secondary
, sdk_type
, kind
));
381 LLDB_LOGF(log
, "%s::%s() aborting creation of platform", class_name
,
387 Status
PlatformAppleSimulator::GetSymbolFile(const FileSpec
&platform_file
,
388 const UUID
*uuid_ptr
,
389 FileSpec
&local_file
) {
391 char platform_file_path
[PATH_MAX
];
392 if (platform_file
.GetPath(platform_file_path
, sizeof(platform_file_path
))) {
393 char resolved_path
[PATH_MAX
];
395 llvm::StringRef sdk
= GetSDKFilepath();
397 ::snprintf(resolved_path
, sizeof(resolved_path
), "%s/%s",
398 sdk
.str().c_str(), platform_file_path
);
400 // First try in the SDK and see if the file is in there
401 local_file
.SetFile(resolved_path
, FileSpec::Style::native
);
402 FileSystem::Instance().Resolve(local_file
);
403 if (FileSystem::Instance().Exists(local_file
))
406 // Else fall back to the actual path itself
407 local_file
.SetFile(platform_file_path
, FileSpec::Style::native
);
408 FileSystem::Instance().Resolve(local_file
);
409 if (FileSystem::Instance().Exists(local_file
))
412 error
= Status::FromErrorStringWithFormatv(
413 "unable to locate a platform file for '{0}' in platform '{1}'",
414 platform_file_path
, GetPluginName());
416 error
= Status::FromErrorString("invalid platform file argument");
421 Status
PlatformAppleSimulator::GetSharedModule(
422 const ModuleSpec
&module_spec
, Process
*process
, ModuleSP
&module_sp
,
423 const FileSpecList
*module_search_paths_ptr
,
424 llvm::SmallVectorImpl
<lldb::ModuleSP
> *old_modules
, bool *did_create_ptr
) {
425 // For iOS/tvOS/watchOS, the SDK files are all cached locally on the
426 // host system. So first we ask for the file in the cached SDK, then
427 // we attempt to get a shared module for the right architecture with
430 ModuleSpec
platform_module_spec(module_spec
);
431 const FileSpec
&platform_file
= module_spec
.GetFileSpec();
432 error
= GetSymbolFile(platform_file
, module_spec
.GetUUIDPtr(),
433 platform_module_spec
.GetFileSpec());
434 if (error
.Success()) {
435 error
= ResolveExecutable(platform_module_spec
, module_sp
,
436 module_search_paths_ptr
);
438 const bool always_create
= false;
439 error
= ModuleList::GetSharedModule(module_spec
, module_sp
,
440 module_search_paths_ptr
, old_modules
,
441 did_create_ptr
, always_create
);
444 module_sp
->SetPlatformFileSpec(platform_file
);
449 uint32_t PlatformAppleSimulator::FindProcesses(
450 const ProcessInstanceInfoMatch
&match_info
,
451 ProcessInstanceInfoList
&process_infos
) {
452 ProcessInstanceInfoList all_osx_process_infos
;
453 // First we get all OSX processes
454 const uint32_t n
= Host::FindProcesses(match_info
, all_osx_process_infos
);
456 // Now we filter them down to only the matching triples.
457 for (uint32_t i
= 0; i
< n
; ++i
) {
458 const ProcessInstanceInfo
&proc_info
= all_osx_process_infos
[i
];
459 const llvm::Triple
&triple
= proc_info
.GetArchitecture().GetTriple();
460 if (triple
.getOS() == m_os_type
&&
461 triple
.getEnvironment() == llvm::Triple::Simulator
) {
462 process_infos
.push_back(proc_info
);
465 return process_infos
.size();
468 /// Whether to skip creating a simulator platform.
469 static bool shouldSkipSimulatorPlatform(bool force
, const ArchSpec
*arch
) {
470 // If the arch is known not to specify a simulator environment, skip creating
471 // the simulator platform (we can create it later if there's a matching arch).
472 // This avoids very slow xcrun queries for non-simulator archs (the slowness
473 // is due to xcrun not caching negative queries.
474 return !force
&& arch
&& arch
->IsValid() &&
475 !arch
->TripleEnvironmentWasSpecified();
478 static const char *g_ios_plugin_name
= "ios-simulator";
479 static const char *g_ios_description
= "iPhone simulator platform plug-in.";
481 /// IPhone Simulator Plugin.
482 struct PlatformiOSSimulator
{
483 static void Initialize() {
484 PluginManager::RegisterPlugin(g_ios_plugin_name
, g_ios_description
,
485 PlatformiOSSimulator::CreateInstance
);
488 static void Terminate() {
489 PluginManager::UnregisterPlugin(PlatformiOSSimulator::CreateInstance
);
492 static PlatformSP
CreateInstance(bool force
, const ArchSpec
*arch
) {
493 if (shouldSkipSimulatorPlatform(force
, arch
))
496 return PlatformAppleSimulator::CreateInstance(
497 "PlatformiOSSimulator", g_ios_description
,
498 ConstString(g_ios_plugin_name
),
499 {llvm::Triple::aarch64
, llvm::Triple::x86_64
, llvm::Triple::x86
},
501 {// Deprecated, but still support Darwin for historical reasons.
502 llvm::Triple::Darwin
, llvm::Triple::MacOSX
,
503 // IOS is not used for simulator triples, but accept it just in
509 "arm64e-apple-ios-simulator", "arm64-apple-ios-simulator",
510 "x86_64-apple-ios-simulator", "x86_64h-apple-ios-simulator",
512 "x86_64h-apple-ios-simulator", "x86_64-apple-ios-simulator",
513 "i386-apple-ios-simulator",
517 "iPhoneSimulator.Internal.sdk", "iPhoneSimulator.sdk",
518 XcodeSDK::Type::iPhoneSimulator
,
519 CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone
, force
, arch
);
523 static const char *g_tvos_plugin_name
= "tvos-simulator";
524 static const char *g_tvos_description
= "tvOS simulator platform plug-in.";
526 /// Apple TV Simulator Plugin.
527 struct PlatformAppleTVSimulator
{
528 static void Initialize() {
529 PluginManager::RegisterPlugin(g_tvos_plugin_name
, g_tvos_description
,
530 PlatformAppleTVSimulator::CreateInstance
);
533 static void Terminate() {
534 PluginManager::UnregisterPlugin(PlatformAppleTVSimulator::CreateInstance
);
537 static PlatformSP
CreateInstance(bool force
, const ArchSpec
*arch
) {
538 if (shouldSkipSimulatorPlatform(force
, arch
))
540 return PlatformAppleSimulator::CreateInstance(
541 "PlatformAppleTVSimulator", g_tvos_description
,
542 ConstString(g_tvos_plugin_name
),
543 {llvm::Triple::aarch64
, llvm::Triple::x86_64
}, llvm::Triple::TvOS
,
544 {llvm::Triple::TvOS
},
548 "arm64e-apple-tvos-simulator", "arm64-apple-tvos-simulator",
549 "x86_64h-apple-tvos-simulator", "x86_64-apple-tvos-simulator",
551 "x86_64h-apple-tvos-simulator", "x86_64-apple-tvos-simulator",
555 "AppleTVSimulator.Internal.sdk", "AppleTVSimulator.sdk",
556 XcodeSDK::Type::AppleTVSimulator
,
557 CoreSimulatorSupport::DeviceType::ProductFamilyID::appleTV
, force
,
563 static const char *g_watchos_plugin_name
= "watchos-simulator";
564 static const char *g_watchos_description
=
565 "Apple Watch simulator platform plug-in.";
567 /// Apple Watch Simulator Plugin.
568 struct PlatformAppleWatchSimulator
{
569 static void Initialize() {
570 PluginManager::RegisterPlugin(g_watchos_plugin_name
, g_watchos_description
,
571 PlatformAppleWatchSimulator::CreateInstance
);
574 static void Terminate() {
575 PluginManager::UnregisterPlugin(
576 PlatformAppleWatchSimulator::CreateInstance
);
579 static PlatformSP
CreateInstance(bool force
, const ArchSpec
*arch
) {
580 if (shouldSkipSimulatorPlatform(force
, arch
))
582 return PlatformAppleSimulator::CreateInstance(
583 "PlatformAppleWatchSimulator", g_watchos_description
,
584 ConstString(g_watchos_plugin_name
),
585 {llvm::Triple::aarch64
, llvm::Triple::x86_64
, llvm::Triple::x86
},
586 llvm::Triple::WatchOS
, {llvm::Triple::WatchOS
},
590 "arm64e-apple-watchos-simulator", "arm64-apple-watchos-simulator",
592 "x86_64-apple-watchos-simulator", "x86_64h-apple-watchos-simulator",
593 "i386-apple-watchos-simulator",
597 "WatchSimulator.Internal.sdk", "WatchSimulator.sdk",
598 XcodeSDK::Type::WatchSimulator
,
599 CoreSimulatorSupport::DeviceType::ProductFamilyID::appleWatch
, force
,
604 static const char *g_xros_plugin_name
= "xros-simulator";
605 static const char *g_xros_description
= "XROS simulator platform plug-in.";
607 /// XRSimulator Plugin.
608 struct PlatformXRSimulator
{
609 static void Initialize() {
610 PluginManager::RegisterPlugin(g_xros_plugin_name
, g_xros_description
,
611 PlatformXRSimulator::CreateInstance
);
614 static void Terminate() {
615 PluginManager::UnregisterPlugin(PlatformXRSimulator::CreateInstance
);
618 static PlatformSP
CreateInstance(bool force
, const ArchSpec
*arch
) {
619 return PlatformAppleSimulator::CreateInstance(
620 "PlatformXRSimulator", g_xros_description
,
621 ConstString(g_xros_plugin_name
),
622 {llvm::Triple::aarch64
, llvm::Triple::x86_64
, llvm::Triple::x86
},
623 llvm::Triple::XROS
, {llvm::Triple::XROS
},
627 "arm64e-apple-xros-simulator", "arm64-apple-xros-simulator",
629 "x86_64-apple-xros-simulator", "x86_64h-apple-xros-simulator",
633 "XRSimulator.Internal.sdk", "XRSimulator.sdk",
634 XcodeSDK::Type::XRSimulator
,
635 CoreSimulatorSupport::DeviceType::ProductFamilyID::appleXR
, force
,
640 static unsigned g_initialize_count
= 0;
643 void PlatformAppleSimulator::Initialize() {
644 if (g_initialize_count
++ == 0) {
645 PlatformDarwin::Initialize();
646 PlatformiOSSimulator::Initialize();
647 PlatformAppleTVSimulator::Initialize();
648 PlatformAppleWatchSimulator::Initialize();
649 PlatformXRSimulator::Initialize();
653 void PlatformAppleSimulator::Terminate() {
654 if (g_initialize_count
> 0)
655 if (--g_initialize_count
== 0) {
656 PlatformXRSimulator::Terminate();
657 PlatformAppleWatchSimulator::Terminate();
658 PlatformAppleTVSimulator::Terminate();
659 PlatformiOSSimulator::Terminate();
660 PlatformDarwin::Terminate();