forked from qt-creator/qt-creator
iOS: Support C++ debugging on devices with iOS 17+
Xcode 16 added special commands for that to lldb: - `device select <uuid>` switches the debugging to that device - `device process attach <options>` attaches the debugger to a running app So we start the application in "waiting state" with the --start-stopped command line parameter, then start the debugger and use these commands to attach. Integration into the lldb bridge needs to directly run the new commands and attach, to get access to the SBTarget. Task-number: QTCREATORBUG-29895 Fixes: QTCREATORBUG-32106 Change-Id: I91abb35c689cbd4d2d9da53afb5a12ddc23e1e9a Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -98,6 +98,7 @@ class Dumper(DumperBase):
|
|||||||
self.startMode_ = None
|
self.startMode_ = None
|
||||||
self.processArgs_ = None
|
self.processArgs_ = None
|
||||||
self.attachPid_ = None
|
self.attachPid_ = None
|
||||||
|
self.deviceUuid_ = None
|
||||||
self.dyldImageSuffix = None
|
self.dyldImageSuffix = None
|
||||||
self.dyldLibraryPath = None
|
self.dyldLibraryPath = None
|
||||||
self.dyldFrameworkPath = None
|
self.dyldFrameworkPath = None
|
||||||
@@ -918,6 +919,7 @@ class Dumper(DumperBase):
|
|||||||
self.environment_ = args.get('environment', [])
|
self.environment_ = args.get('environment', [])
|
||||||
self.environment_ = list(map(lambda x: self.hexdecode(x), self.environment_))
|
self.environment_ = list(map(lambda x: self.hexdecode(x), self.environment_))
|
||||||
self.attachPid_ = args.get('attachpid', 0)
|
self.attachPid_ = args.get('attachpid', 0)
|
||||||
|
self.deviceUuid_ = args.get('deviceUuid', '')
|
||||||
self.sysRoot_ = args.get('sysroot', '')
|
self.sysRoot_ = args.get('sysroot', '')
|
||||||
self.remoteChannel_ = args.get('remotechannel', '')
|
self.remoteChannel_ = args.get('remotechannel', '')
|
||||||
self.platform_ = args.get('platform', '')
|
self.platform_ = args.get('platform', '')
|
||||||
@@ -942,6 +944,13 @@ class Dumper(DumperBase):
|
|||||||
if self.startMode_ == DebuggerStartMode.AttachExternal:
|
if self.startMode_ == DebuggerStartMode.AttachExternal:
|
||||||
self.symbolFile_ = ''
|
self.symbolFile_ = ''
|
||||||
|
|
||||||
|
if self.startMode_ == DebuggerStartMode.AttachToIosDevice:
|
||||||
|
# The script code depends on a target from now on,
|
||||||
|
# so we already need to attach with the special Apple lldb debugger commands
|
||||||
|
self.runDebuggerCommand('device select ' + self.deviceUuid_)
|
||||||
|
self.runDebuggerCommand('device process attach -p ' + str(self.attachPid_))
|
||||||
|
self.target = self.debugger.GetSelectedTarget()
|
||||||
|
else:
|
||||||
self.target = self.debugger.CreateTarget(
|
self.target = self.debugger.CreateTarget(
|
||||||
self.symbolFile_, None, self.platform_, True, error)
|
self.symbolFile_, None, self.platform_, True, error)
|
||||||
|
|
||||||
@@ -950,6 +959,11 @@ class Dumper(DumperBase):
|
|||||||
self.reportState('enginerunfailed')
|
self.reportState('enginerunfailed')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not self.target:
|
||||||
|
self.report('Debugger failed to create target.')
|
||||||
|
self.reportState('enginerunfailed')
|
||||||
|
return
|
||||||
|
|
||||||
broadcaster = self.target.GetBroadcaster()
|
broadcaster = self.target.GetBroadcaster()
|
||||||
listener = self.debugger.GetListener()
|
listener = self.debugger.GetListener()
|
||||||
broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged)
|
broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged)
|
||||||
@@ -1090,6 +1104,11 @@ class Dumper(DumperBase):
|
|||||||
self.reportState('enginerunokandinferiorunrunnable')
|
self.reportState('enginerunokandinferiorunrunnable')
|
||||||
else:
|
else:
|
||||||
self.reportState('enginerunfailed')
|
self.reportState('enginerunfailed')
|
||||||
|
elif self.startMode_ == DebuggerStartMode.AttachToIosDevice:
|
||||||
|
# Already attached in setupInferior (to get a SBTarget),
|
||||||
|
# just get the process from it
|
||||||
|
self.process = self.target.GetProcess()
|
||||||
|
self.reportState('enginerunandinferiorrunok')
|
||||||
else:
|
else:
|
||||||
launchInfo = lldb.SBLaunchInfo(self.processArgs_)
|
launchInfo = lldb.SBLaunchInfo(self.processArgs_)
|
||||||
launchInfo.SetWorkingDirectory(self.workingDirectory_)
|
launchInfo.SetWorkingDirectory(self.workingDirectory_)
|
||||||
@@ -1920,16 +1939,20 @@ class Dumper(DumperBase):
|
|||||||
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
||||||
self.reportResult('fulltrace="%s"' % self.hexencode(result.GetOutput()), args)
|
self.reportResult('fulltrace="%s"' % self.hexencode(result.GetOutput()), args)
|
||||||
|
|
||||||
def executeDebuggerCommand(self, args):
|
def runDebuggerCommand(self, command):
|
||||||
self.reportToken(args)
|
self.report('Running debugger command "{}"'.format(command))
|
||||||
result = lldb.SBCommandReturnObject()
|
result = lldb.SBCommandReturnObject()
|
||||||
command = args['command']
|
|
||||||
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
||||||
success = result.Succeeded()
|
success = result.Succeeded()
|
||||||
output = toCString(result.GetOutput())
|
output = toCString(result.GetOutput())
|
||||||
error = toCString(str(result.GetError()))
|
error = toCString(str(result.GetError()))
|
||||||
self.report('success="%d",output="%s",error="%s"' % (success, output, error))
|
self.report('success="%d",output="%s",error="%s"' % (success, output, error))
|
||||||
|
|
||||||
|
def executeDebuggerCommand(self, args):
|
||||||
|
self.reportToken(args)
|
||||||
|
command = args['command']
|
||||||
|
self.runDebuggerCommand(command)
|
||||||
|
|
||||||
def executeRoundtrip(self, args):
|
def executeRoundtrip(self, args):
|
||||||
self.reportResult('', args)
|
self.reportResult('', args)
|
||||||
|
|
||||||
|
@@ -15,8 +15,10 @@ class DebuggerStartMode():
|
|||||||
AttachCore,
|
AttachCore,
|
||||||
AttachToRemoteServer,
|
AttachToRemoteServer,
|
||||||
AttachToRemoteProcess,
|
AttachToRemoteProcess,
|
||||||
|
AttachToQmlServer,
|
||||||
StartRemoteProcess,
|
StartRemoteProcess,
|
||||||
) = range(0, 9)
|
AttachToIosDevice
|
||||||
|
) = range(0, 11)
|
||||||
|
|
||||||
|
|
||||||
# Known special formats. Keep in sync with DisplayFormat in debuggerprotocol.h
|
# Known special formats. Keep in sync with DisplayFormat in debuggerprotocol.h
|
||||||
|
@@ -28,9 +28,8 @@ const char ANALYZERTASK_ID[] = "Analyzer.TaskId";
|
|||||||
|
|
||||||
} // namespace Constants
|
} // namespace Constants
|
||||||
|
|
||||||
// Keep in sync with dumper.py
|
// Keep in sync with debugger/utils.py
|
||||||
enum DebuggerStartMode
|
enum DebuggerStartMode {
|
||||||
{
|
|
||||||
NoStartMode,
|
NoStartMode,
|
||||||
StartInternal, // Start current start project's binary
|
StartInternal, // Start current start project's binary
|
||||||
StartExternal, // Start binary found in file system
|
StartExternal, // Start binary found in file system
|
||||||
@@ -40,7 +39,8 @@ enum DebuggerStartMode
|
|||||||
AttachToRemoteServer, // Attach to a running gdbserver
|
AttachToRemoteServer, // Attach to a running gdbserver
|
||||||
AttachToRemoteProcess, // Attach to a running remote process
|
AttachToRemoteProcess, // Attach to a running remote process
|
||||||
AttachToQmlServer, // Attach to a running QmlServer
|
AttachToQmlServer, // Attach to a running QmlServer
|
||||||
StartRemoteProcess // Start and attach to a remote process
|
StartRemoteProcess, // Start and attach to a remote process
|
||||||
|
AttachToIosDevice // Attach to an application on a iOS 17+ device
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DebuggerCloseMode
|
enum DebuggerCloseMode
|
||||||
|
@@ -116,6 +116,8 @@ public:
|
|||||||
QString deviceSymbolsRoot;
|
QString deviceSymbolsRoot;
|
||||||
bool continueAfterAttach = false;
|
bool continueAfterAttach = false;
|
||||||
Utils::FilePath sysRoot;
|
Utils::FilePath sysRoot;
|
||||||
|
// iOS 17+
|
||||||
|
QString deviceUuid;
|
||||||
|
|
||||||
// Used by general core file debugging. Public access requested in QTCREATORBUG-17158.
|
// Used by general core file debugging. Public access requested in QTCREATORBUG-17158.
|
||||||
Utils::FilePath coreFile;
|
Utils::FilePath coreFile;
|
||||||
|
@@ -255,6 +255,11 @@ void DebuggerRunTool::setDeviceSymbolsRoot(const QString &deviceSymbolsRoot)
|
|||||||
m_runParameters.deviceSymbolsRoot = deviceSymbolsRoot;
|
m_runParameters.deviceSymbolsRoot = deviceSymbolsRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebuggerRunTool::setDeviceUuid(const QString &uuid)
|
||||||
|
{
|
||||||
|
m_runParameters.deviceUuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
void DebuggerRunTool::setTestCase(int testCase)
|
void DebuggerRunTool::setTestCase(int testCase)
|
||||||
{
|
{
|
||||||
m_runParameters.testCase = testCase;
|
m_runParameters.testCase = testCase;
|
||||||
|
@@ -101,6 +101,8 @@ public:
|
|||||||
|
|
||||||
void setIosPlatform(const QString &platform);
|
void setIosPlatform(const QString &platform);
|
||||||
void setDeviceSymbolsRoot(const QString &deviceSymbolsRoot);
|
void setDeviceSymbolsRoot(const QString &deviceSymbolsRoot);
|
||||||
|
void setDeviceUuid(const QString &uuid);
|
||||||
|
|
||||||
void setAbi(const ProjectExplorer::Abi &abi);
|
void setAbi(const ProjectExplorer::Abi &abi);
|
||||||
|
|
||||||
DebuggerEngineType cppEngineType() const;
|
DebuggerEngineType cppEngineType() const;
|
||||||
|
@@ -270,6 +270,7 @@ void LldbEngine::handleLldbStarted()
|
|||||||
cmd2.arg("startmode", rp.startMode);
|
cmd2.arg("startmode", rp.startMode);
|
||||||
cmd2.arg("nativemixed", isNativeMixedActive());
|
cmd2.arg("nativemixed", isNativeMixedActive());
|
||||||
cmd2.arg("workingdirectory", inferior.workingDirectory.path());
|
cmd2.arg("workingdirectory", inferior.workingDirectory.path());
|
||||||
|
cmd2.arg("deviceUuid", rp.deviceUuid);
|
||||||
Environment environment = inferior.environment;
|
Environment environment = inferior.environment;
|
||||||
// Prevent lldb from automatically setting OS_ACTIVITY_DT_MODE to mirror
|
// Prevent lldb from automatically setting OS_ACTIVITY_DT_MODE to mirror
|
||||||
// NSLog to stderr, as that will also mirror os_log, which we pick up in
|
// NSLog to stderr, as that will also mirror os_log, which we pick up in
|
||||||
@@ -303,18 +304,20 @@ void LldbEngine::handleLldbStarted()
|
|||||||
if (rp.startMode != StartInternal) {
|
if (rp.startMode != StartInternal) {
|
||||||
// it is better not to check the start mode on the python sid (as we would have to duplicate the
|
// it is better not to check the start mode on the python sid (as we would have to duplicate the
|
||||||
// enum values), and thus we assume that if the rp.attachPID is valid we really have to attach
|
// enum values), and thus we assume that if the rp.attachPID is valid we really have to attach
|
||||||
QTC_CHECK(rp.attachPID.isValid() && (rp.startMode == AttachToRemoteProcess
|
QTC_CHECK(
|
||||||
|| rp.startMode == AttachToLocalProcess
|
rp.attachPID.isValid()
|
||||||
|| rp.startMode == AttachToRemoteServer));
|
&& (rp.startMode == AttachToRemoteProcess || rp.startMode == AttachToLocalProcess
|
||||||
|
|| rp.startMode == AttachToRemoteServer || rp.startMode == AttachToIosDevice));
|
||||||
cmd2.arg("attachpid", rp.attachPID.pid());
|
cmd2.arg("attachpid", rp.attachPID.pid());
|
||||||
cmd2.arg("sysroot", rp.deviceSymbolsRoot.isEmpty() ? rp.sysRoot.toString()
|
cmd2.arg("sysroot", rp.deviceSymbolsRoot.isEmpty() ? rp.sysRoot.toString()
|
||||||
: rp.deviceSymbolsRoot);
|
: rp.deviceSymbolsRoot);
|
||||||
cmd2.arg("remotechannel", ((rp.startMode == AttachToRemoteProcess
|
cmd2.arg("remotechannel", ((rp.startMode == AttachToRemoteProcess
|
||||||
|| rp.startMode == AttachToRemoteServer)
|
|| rp.startMode == AttachToRemoteServer)
|
||||||
? rp.remoteChannel : QString()));
|
? rp.remoteChannel : QString()));
|
||||||
QTC_CHECK(!rp.continueAfterAttach || (rp.startMode == AttachToRemoteProcess
|
QTC_CHECK(
|
||||||
|| rp.startMode == AttachToLocalProcess
|
!rp.continueAfterAttach
|
||||||
|| rp.startMode == AttachToRemoteServer));
|
|| (rp.startMode == AttachToRemoteProcess || rp.startMode == AttachToLocalProcess
|
||||||
|
|| rp.startMode == AttachToRemoteServer || rp.startMode == AttachToIosDevice));
|
||||||
m_continueAtNextSpontaneousStop = false;
|
m_continueAtNextSpontaneousStop = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -103,7 +103,8 @@ bool IosRunConfiguration::isEnabled(Id runMode) const
|
|||||||
|
|
||||||
IosDevice::ConstPtr iosdevice = std::dynamic_pointer_cast<const IosDevice>(dev);
|
IosDevice::ConstPtr iosdevice = std::dynamic_pointer_cast<const IosDevice>(dev);
|
||||||
if (iosdevice && iosdevice->handler() == IosDevice::Handler::DeviceCtl
|
if (iosdevice && iosdevice->handler() == IosDevice::Handler::DeviceCtl
|
||||||
&& runMode != ProjectExplorer::Constants::NORMAL_RUN_MODE) {
|
&& runMode != ProjectExplorer::Constants::NORMAL_RUN_MODE
|
||||||
|
&& !IosDeviceManager::isDeviceCtlDebugSupported()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,9 +281,9 @@ QString IosRunConfiguration::disabledReason(Id runMode) const
|
|||||||
}
|
}
|
||||||
IosDevice::ConstPtr iosdevice = std::dynamic_pointer_cast<const IosDevice>(dev);
|
IosDevice::ConstPtr iosdevice = std::dynamic_pointer_cast<const IosDevice>(dev);
|
||||||
if (iosdevice && iosdevice->handler() == IosDevice::Handler::DeviceCtl
|
if (iosdevice && iosdevice->handler() == IosDevice::Handler::DeviceCtl
|
||||||
&& runMode != ProjectExplorer::Constants::NORMAL_RUN_MODE) {
|
&& runMode != ProjectExplorer::Constants::NORMAL_RUN_MODE
|
||||||
return Tr::tr("Debugging and profiling is currently not supported for devices with iOS "
|
&& !IosDeviceManager::isDeviceCtlDebugSupported()) {
|
||||||
"17 and later.");
|
return Tr::tr("Debugging on devices with iOS 17 and later requires Xcode 16 or later.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return RunConfiguration::disabledReason(runMode);
|
return RunConfiguration::disabledReason(runMode);
|
||||||
|
@@ -92,6 +92,12 @@ static void stopRunningRunControl(RunControl *runControl)
|
|||||||
activeRunControls[devId] = runControl;
|
activeRunControls[devId] = runControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString getBundleIdentifier(const FilePath &bundlePath)
|
||||||
|
{
|
||||||
|
QSettings settings(bundlePath.pathAppended("Info.plist").toString(), QSettings::NativeFormat);
|
||||||
|
return settings.value(QString::fromLatin1("CFBundleIdentifier")).toString();
|
||||||
|
}
|
||||||
|
|
||||||
struct AppInfo
|
struct AppInfo
|
||||||
{
|
{
|
||||||
QUrl pathOnDevice;
|
QUrl pathOnDevice;
|
||||||
@@ -104,16 +110,18 @@ public:
|
|||||||
DeviceCtlRunnerBase(RunControl *runControl);
|
DeviceCtlRunnerBase(RunControl *runControl);
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
qint64 processIdentifier() const { return m_processIdentifier; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
GroupItem findApp(const QString &bundleIdentifier, Storage<AppInfo> appInfo);
|
||||||
|
GroupItem findProcess(Storage<AppInfo> &appInfo);
|
||||||
void reportStoppedImpl();
|
void reportStoppedImpl();
|
||||||
|
|
||||||
IosDevice::ConstPtr m_device;
|
IosDevice::ConstPtr m_device;
|
||||||
QStringList m_arguments;
|
QStringList m_arguments;
|
||||||
|
qint64 m_processIdentifier = -1;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GroupItem findApp(const QString &bundleIdentifier, Storage<AppInfo> appInfo);
|
|
||||||
GroupItem findProcess(Storage<AppInfo> &appInfo);
|
|
||||||
GroupItem killProcess(Storage<AppInfo> &appInfo);
|
GroupItem killProcess(Storage<AppInfo> &appInfo);
|
||||||
virtual GroupItem launchTask(const QString &bundleIdentifier) = 0;
|
virtual GroupItem launchTask(const QString &bundleIdentifier) = 0;
|
||||||
|
|
||||||
@@ -135,7 +143,6 @@ private:
|
|||||||
std::unique_ptr<TaskTree> m_stopTask;
|
std::unique_ptr<TaskTree> m_stopTask;
|
||||||
std::unique_ptr<TaskTree> m_pollTask;
|
std::unique_ptr<TaskTree> m_pollTask;
|
||||||
QTimer m_pollTimer;
|
QTimer m_pollTimer;
|
||||||
qint64 m_processIdentifier = -1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeviceCtlRunner final : public DeviceCtlRunnerBase
|
class DeviceCtlRunner final : public DeviceCtlRunnerBase
|
||||||
@@ -145,11 +152,15 @@ public:
|
|||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
void setStartStopped(bool startStopped) { m_startStopped = startStopped; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GroupItem launchTask(const QString &bundleIdentifier);
|
GroupItem launchTask(const QString &bundleIdentifier);
|
||||||
|
|
||||||
Process m_process;
|
Process m_process;
|
||||||
std::unique_ptr<TemporaryFile> m_deviceCtlOutput;
|
std::unique_ptr<TemporaryFile> m_deviceCtlOutput;
|
||||||
|
std::unique_ptr<TaskTree> m_processIdTask;
|
||||||
|
bool m_startStopped = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceCtlRunnerBase::DeviceCtlRunnerBase(RunControl *runControl)
|
DeviceCtlRunnerBase::DeviceCtlRunnerBase(RunControl *runControl)
|
||||||
@@ -305,9 +316,7 @@ void DeviceCtlRunnerBase::reportStoppedImpl()
|
|||||||
|
|
||||||
void DeviceCtlRunnerBase::start()
|
void DeviceCtlRunnerBase::start()
|
||||||
{
|
{
|
||||||
QSettings settings(m_bundlePath.pathAppended("Info.plist").toString(), QSettings::NativeFormat);
|
const QString bundleIdentifier = getBundleIdentifier(m_bundlePath);
|
||||||
const QString bundleIdentifier
|
|
||||||
= settings.value(QString::fromLatin1("CFBundleIdentifier")).toString();
|
|
||||||
if (bundleIdentifier.isEmpty()) {
|
if (bundleIdentifier.isEmpty()) {
|
||||||
reportFailure(Tr::tr("Failed to determine bundle identifier."));
|
reportFailure(Tr::tr("Failed to determine bundle identifier."));
|
||||||
return;
|
return;
|
||||||
@@ -450,8 +459,9 @@ GroupItem DeviceCtlRunner::launchTask(const QString &bundleIdentifier)
|
|||||||
reportFailure(Tr::tr("Running failed. Failed to create the temporary output file."));
|
reportFailure(Tr::tr("Running failed. Failed to create the temporary output file."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_process.setCommand(
|
const QStringList startStoppedArg = m_startStopped ? QStringList("--start-stopped")
|
||||||
{FilePath::fromString("/usr/bin/xcrun"),
|
: QStringList();
|
||||||
|
const QStringList arguments = QStringList(
|
||||||
{"devicectl",
|
{"devicectl",
|
||||||
"device",
|
"device",
|
||||||
"process",
|
"process",
|
||||||
@@ -460,15 +470,36 @@ GroupItem DeviceCtlRunner::launchTask(const QString &bundleIdentifier)
|
|||||||
m_device->uniqueInternalDeviceId(),
|
m_device->uniqueInternalDeviceId(),
|
||||||
"--quiet",
|
"--quiet",
|
||||||
"--json-output",
|
"--json-output",
|
||||||
m_deviceCtlOutput->fileName(),
|
m_deviceCtlOutput->fileName()})
|
||||||
"--console",
|
+ startStoppedArg
|
||||||
bundleIdentifier,
|
+ QStringList({"--console", bundleIdentifier}) + m_arguments;
|
||||||
m_arguments}});
|
m_process.setCommand({FilePath::fromString("/usr/bin/xcrun"), arguments});
|
||||||
connect(&m_process, &Process::started, this, [this] { reportStarted(); });
|
connect(&m_process, &Process::started, this, [this, bundleIdentifier] {
|
||||||
|
// devicectl does report the process ID in its json output, but that is broken
|
||||||
|
// for --console. When that is used, the json output is only written after the process
|
||||||
|
// finished, which is not helpful.
|
||||||
|
// Manually find the process ID for the bundle identifier.
|
||||||
|
Storage<AppInfo> appInfo;
|
||||||
|
m_processIdTask.reset(new TaskTree(Group{
|
||||||
|
sequential,
|
||||||
|
appInfo,
|
||||||
|
findApp(bundleIdentifier, appInfo),
|
||||||
|
findProcess(appInfo),
|
||||||
|
onGroupDone([this, appInfo](DoneWith doneWith) {
|
||||||
|
if (doneWith == DoneWith::Success) {
|
||||||
|
m_processIdentifier = appInfo->processIdentifier;
|
||||||
|
reportStarted();
|
||||||
|
} else {
|
||||||
|
reportFailure(Tr::tr("Failed to retrieve process ID."));
|
||||||
|
}
|
||||||
|
})}));
|
||||||
|
m_processIdTask->start();
|
||||||
|
});
|
||||||
connect(&m_process, &Process::done, this, [this] {
|
connect(&m_process, &Process::done, this, [this] {
|
||||||
if (m_process.error() != QProcess::UnknownError)
|
if (m_process.error() != QProcess::UnknownError)
|
||||||
reportFailure(Tr::tr("Failed to run devicectl: %1.").arg(m_process.errorString()));
|
reportFailure(Tr::tr("Failed to run devicectl: %1.").arg(m_process.errorString()));
|
||||||
m_deviceCtlOutput->reset();
|
m_deviceCtlOutput->reset();
|
||||||
|
m_processIdTask.reset();
|
||||||
reportStoppedImpl();
|
reportStoppedImpl();
|
||||||
});
|
});
|
||||||
connect(&m_process, &Process::readyReadStandardError, this, [this] {
|
connect(&m_process, &Process::readyReadStandardError, this, [this] {
|
||||||
@@ -841,7 +872,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
void start() override;
|
void start() override;
|
||||||
|
|
||||||
IosRunner *m_runner;
|
IosRunner *m_iosRunner = nullptr;
|
||||||
|
DeviceCtlRunner *m_deviceCtlRunner = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static expected_str<FilePath> findDeviceSdk(IosDevice::ConstPtr dev)
|
static expected_str<FilePath> findDeviceSdk(IosDevice::ConstPtr dev)
|
||||||
@@ -874,15 +906,29 @@ IosDebugSupport::IosDebugSupport(RunControl *runControl)
|
|||||||
{
|
{
|
||||||
setId("IosDebugSupport");
|
setId("IosDebugSupport");
|
||||||
|
|
||||||
m_runner = new IosRunner(runControl);
|
IosDevice::ConstPtr dev = std::dynamic_pointer_cast<const IosDevice>(device());
|
||||||
m_runner->setCppDebugging(isCppDebugging());
|
|
||||||
m_runner->setQmlDebugging(isQmlDebugging() ? QmlDebuggerServices : NoQmlDebugServices);
|
|
||||||
|
|
||||||
addStartDependency(m_runner);
|
if (dev->type() == Ios::Constants::IOS_SIMULATOR_TYPE
|
||||||
|
|| dev->handler() == IosDevice::Handler::IosTool) {
|
||||||
|
m_iosRunner = new IosRunner(runControl);
|
||||||
|
m_iosRunner->setCppDebugging(isCppDebugging());
|
||||||
|
m_iosRunner->setQmlDebugging(isQmlDebugging() ? QmlDebuggerServices : NoQmlDebugServices);
|
||||||
|
addStartDependency(m_iosRunner);
|
||||||
|
} else {
|
||||||
|
QTC_CHECK(isCppDebugging());
|
||||||
|
m_deviceCtlRunner = new DeviceCtlRunner(runControl);
|
||||||
|
m_deviceCtlRunner->setStartStopped(true);
|
||||||
|
addStartDependency(m_deviceCtlRunner);
|
||||||
|
}
|
||||||
|
|
||||||
if (device()->type() == Ios::Constants::IOS_DEVICE_TYPE) {
|
if (device()->type() == Ios::Constants::IOS_DEVICE_TYPE) {
|
||||||
IosDevice::ConstPtr dev = std::dynamic_pointer_cast<const IosDevice>(device());
|
if (dev->handler() == IosDevice::Handler::DeviceCtl) {
|
||||||
|
QTC_CHECK(IosDeviceManager::isDeviceCtlDebugSupported());
|
||||||
|
setStartMode(AttachToIosDevice);
|
||||||
|
setDeviceUuid(dev->uniqueInternalDeviceId());
|
||||||
|
} else {
|
||||||
setStartMode(AttachToRemoteProcess);
|
setStartMode(AttachToRemoteProcess);
|
||||||
|
}
|
||||||
setIosPlatform("remote-ios");
|
setIosPlatform("remote-ios");
|
||||||
const expected_str<FilePath> deviceSdk = findDeviceSdk(dev);
|
const expected_str<FilePath> deviceSdk = findDeviceSdk(dev);
|
||||||
|
|
||||||
@@ -898,20 +944,39 @@ IosDebugSupport::IosDebugSupport(RunControl *runControl)
|
|||||||
|
|
||||||
void IosDebugSupport::start()
|
void IosDebugSupport::start()
|
||||||
{
|
{
|
||||||
if (!m_runner->isAppRunning()) {
|
const IosDeviceTypeAspect::Data *data = runControl()->aspectData<IosDeviceTypeAspect>();
|
||||||
|
QTC_ASSERT(data, reportFailure("Broken IosDeviceTypeAspect setup."); return);
|
||||||
|
setRunControlName(data->applicationName);
|
||||||
|
setContinueAfterAttach(true);
|
||||||
|
|
||||||
|
IosDevice::ConstPtr dev = std::dynamic_pointer_cast<const IosDevice>(device());
|
||||||
|
if (dev->type() == Ios::Constants::IOS_DEVICE_TYPE
|
||||||
|
&& dev->handler() == IosDevice::Handler::DeviceCtl) {
|
||||||
|
const auto msgOnlyCppDebuggingSupported = [] {
|
||||||
|
return Tr::tr("Only C++ debugging is supported for devices with iOS 17 and later.");
|
||||||
|
};
|
||||||
|
if (!isCppDebugging()) {
|
||||||
|
reportFailure(msgOnlyCppDebuggingSupported());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isQmlDebugging()) {
|
||||||
|
runParameters().isQmlDebugging = false;
|
||||||
|
appendMessage(msgOnlyCppDebuggingSupported(), OutputFormat::LogMessageFormat, true);
|
||||||
|
}
|
||||||
|
setAttachPid(m_deviceCtlRunner->processIdentifier());
|
||||||
|
setInferiorExecutable(data->localExecutable);
|
||||||
|
DebuggerRunTool::start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_iosRunner->isAppRunning()) {
|
||||||
reportFailure(Tr::tr("Application not running."));
|
reportFailure(Tr::tr("Application not running."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IosDeviceTypeAspect::Data *data = runControl()->aspectData<IosDeviceTypeAspect>();
|
Port gdbServerPort = m_iosRunner->gdbServerPort();
|
||||||
QTC_ASSERT(data, reportFailure("Broken IosDeviceTypeAspect setup."); return);
|
Port qmlServerPort = m_iosRunner->qmlServerPort();
|
||||||
|
setAttachPid(ProcessHandle(m_iosRunner->pid()));
|
||||||
setRunControlName(data->applicationName);
|
|
||||||
setContinueAfterAttach(true);
|
|
||||||
|
|
||||||
Port gdbServerPort = m_runner->gdbServerPort();
|
|
||||||
Port qmlServerPort = m_runner->qmlServerPort();
|
|
||||||
setAttachPid(ProcessHandle(m_runner->pid()));
|
|
||||||
|
|
||||||
const bool cppDebug = isCppDebugging();
|
const bool cppDebug = isCppDebugging();
|
||||||
const bool qmlDebug = isQmlDebugging();
|
const bool qmlDebug = isQmlDebugging();
|
||||||
|
Reference in New Issue
Block a user