forked from qt-creator/qt-creator
Debugging on Android
This implements the host side of https://codereview.qt-project.org/#change,50290 Change-Id: I13c7df29534a2a85202c2b295b139896443b0120 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com> Reviewed-by: Daniel Teske <daniel.teske@digia.com> Reviewed-by: BogDan Vatra <bogdan@kde.org> Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
@@ -135,7 +135,11 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig,
|
|||||||
m_runner, SLOT(start()));
|
m_runner, SLOT(start()));
|
||||||
connect(m_runControl, SIGNAL(finished()),
|
connect(m_runControl, SIGNAL(finished()),
|
||||||
m_runner, SLOT(stop()));
|
m_runner, SLOT(stop()));
|
||||||
|
connect(m_runControl->engine(), SIGNAL(aboutToNotifyInferiorSetupOk()),
|
||||||
|
m_runner, SLOT(handleGdbRunning()));
|
||||||
|
|
||||||
|
connect(m_runner, SIGNAL(remoteServerRunning(QByteArray,int)),
|
||||||
|
SLOT(handleRemoteServerRunning(QByteArray,int)));
|
||||||
connect(m_runner, SIGNAL(remoteProcessStarted(int,int)),
|
connect(m_runner, SIGNAL(remoteProcessStarted(int,int)),
|
||||||
SLOT(handleRemoteProcessStarted(int,int)));
|
SLOT(handleRemoteProcessStarted(int,int)));
|
||||||
connect(m_runner, SIGNAL(remoteProcessFinished(QString)),
|
connect(m_runner, SIGNAL(remoteProcessFinished(QString)),
|
||||||
@@ -147,6 +151,11 @@ AndroidDebugSupport::AndroidDebugSupport(AndroidRunConfiguration *runConfig,
|
|||||||
SLOT(handleRemoteOutput(QByteArray)));
|
SLOT(handleRemoteOutput(QByteArray)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidDebugSupport::handleRemoteServerRunning(const QByteArray &serverChannel, int pid)
|
||||||
|
{
|
||||||
|
m_runControl->engine()->notifyEngineRemoteServerRunning(serverChannel, pid);
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidDebugSupport::handleRemoteProcessStarted(int gdbServerPort, int qmlPort)
|
void AndroidDebugSupport::handleRemoteProcessStarted(int gdbServerPort, int qmlPort)
|
||||||
{
|
{
|
||||||
disconnect(m_runner, SIGNAL(remoteProcessStarted(int,int)),
|
disconnect(m_runner, SIGNAL(remoteProcessStarted(int,int)),
|
||||||
|
@@ -53,7 +53,8 @@ public:
|
|||||||
Debugger::DebuggerRunControl *runControl);
|
Debugger::DebuggerRunControl *runControl);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleRemoteProcessStarted(int gdbServerPort = -1, int qmlPort = -1);
|
void handleRemoteServerRunning(const QByteArray &serverChannel, int pid);
|
||||||
|
void handleRemoteProcessStarted(int gdbServerPort, int qmlPort);
|
||||||
void handleRemoteProcessFinished(const QString &errorMsg);
|
void handleRemoteProcessFinished(const QString &errorMsg);
|
||||||
|
|
||||||
void handleRemoteOutput(const QByteArray &output);
|
void handleRemoteOutput(const QByteArray &output);
|
||||||
|
@@ -36,19 +36,27 @@
|
|||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
|
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
typedef QLatin1String _;
|
||||||
|
|
||||||
AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig, bool debuggingMode)
|
AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig, bool debuggingMode)
|
||||||
: QThread(parent)
|
: QThread(parent)
|
||||||
{
|
{
|
||||||
|
m_wasStarted = false;
|
||||||
m_useCppDebugger = debuggingMode && runConfig->debuggerAspect()->useCppDebugger();
|
m_useCppDebugger = debuggingMode && runConfig->debuggerAspect()->useCppDebugger();
|
||||||
m_useQmlDebugger = debuggingMode && runConfig->debuggerAspect()->useQmlDebugger();
|
m_useQmlDebugger = debuggingMode && runConfig->debuggerAspect()->useQmlDebugger();
|
||||||
m_remoteGdbChannel = runConfig->remoteChannel();
|
QString channel = runConfig->remoteChannel();
|
||||||
|
QTC_CHECK(channel.startsWith(QLatin1Char(':')));
|
||||||
|
m_localGdbServerPort = channel.mid(1).toUShort();
|
||||||
|
QTC_CHECK(m_localGdbServerPort);
|
||||||
m_qmlPort = runConfig->debuggerAspect()->qmlDebugServerPort();
|
m_qmlPort = runConfig->debuggerAspect()->qmlDebugServerPort();
|
||||||
ProjectExplorer::Target *target = runConfig->target();
|
ProjectExplorer::Target *target = runConfig->target();
|
||||||
AndroidDeployStep *ds = runConfig->deployStep();
|
AndroidDeployStep *ds = runConfig->deployStep();
|
||||||
@@ -61,204 +69,258 @@ AndroidRunner::AndroidRunner(QObject *parent, AndroidRunConfiguration *runConfig
|
|||||||
m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));
|
m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));
|
||||||
m_deviceSerialNumber = ds->deviceSerialNumber();
|
m_deviceSerialNumber = ds->deviceSerialNumber();
|
||||||
m_processPID = -1;
|
m_processPID = -1;
|
||||||
m_gdbserverPID = -1;
|
m_adb = AndroidConfigurations::instance().adbToolPath().toString();
|
||||||
connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
|
m_selector = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
|
||||||
|
|
||||||
|
QString packageDir = _("/data/data/") + m_packageName;
|
||||||
|
m_pingFile = packageDir + _("/debug-ping");
|
||||||
|
m_pongFile = _("/data/local/tmp/qt/debug-pong-") + m_packageName;
|
||||||
|
m_gdbserverSocket = packageDir + _("/debug-socket");
|
||||||
|
m_gdbserverPath = packageDir + _("/lib/gdbserver");
|
||||||
|
m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket;
|
||||||
|
|
||||||
|
// Detect busybox, as we need to pass -w to ps to get wide output.
|
||||||
|
QProcess psProc;
|
||||||
|
psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)"));
|
||||||
|
psProc.waitForFinished();
|
||||||
|
QByteArray which = psProc.readAll();
|
||||||
|
m_isBusyBox = which.startsWith("busybox");
|
||||||
|
|
||||||
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
|
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
|
||||||
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()) , SLOT(logcatReadStandardError()));
|
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()), SLOT(logcatReadStandardError()));
|
||||||
|
connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidRunner::~AndroidRunner()
|
AndroidRunner::~AndroidRunner()
|
||||||
{
|
{
|
||||||
stop();
|
//stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extractPidFromChunk(const QByteArray &chunk, int from)
|
||||||
|
{
|
||||||
|
int pos1 = chunk.indexOf(' ', from);
|
||||||
|
if (pos1 == -1)
|
||||||
|
return -1;
|
||||||
|
while (chunk[pos1] == ' ')
|
||||||
|
++pos1;
|
||||||
|
int pos3 = chunk.indexOf(' ', pos1);
|
||||||
|
int pid = chunk.mid(pos1, pos3 - pos1).toInt();
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extractPid(const QString &exeName, const QByteArray &psOutput)
|
||||||
|
{
|
||||||
|
const QByteArray needle = exeName.toUtf8() + '\r';
|
||||||
|
const int to = psOutput.indexOf(needle);
|
||||||
|
if (to == -1)
|
||||||
|
return -1;
|
||||||
|
const int from = psOutput.lastIndexOf('\n', to);
|
||||||
|
if (from == -1)
|
||||||
|
return -1;
|
||||||
|
return extractPidFromChunk(psOutput, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AndroidRunner::runPs()
|
||||||
|
{
|
||||||
|
QProcess psProc;
|
||||||
|
QStringList args = m_selector;
|
||||||
|
args << _("shell") << _("ps");
|
||||||
|
if (m_isBusyBox)
|
||||||
|
args << _("-w");
|
||||||
|
|
||||||
|
psProc.start(m_adb, args);
|
||||||
|
psProc.waitForFinished();
|
||||||
|
return psProc.readAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::checkPID()
|
void AndroidRunner::checkPID()
|
||||||
{
|
{
|
||||||
QProcess psProc;
|
if (!m_wasStarted)
|
||||||
QLatin1String psCmd = QLatin1String("ps");
|
|
||||||
QLatin1String psPidRx = QLatin1String("\\d+\\s+(\\d+)");
|
|
||||||
|
|
||||||
// Detect busybox, as we need to pass -w to it to get wide output.
|
|
||||||
psProc.start(AndroidConfigurations::instance().adbToolPath().toString(),
|
|
||||||
AndroidDeviceInfo::adbSelector(m_deviceSerialNumber)
|
|
||||||
<< QLatin1String("shell") << QLatin1String("readlink") << QLatin1String("$(which ps)"));
|
|
||||||
if (!psProc.waitForFinished(-1)) {
|
|
||||||
psProc.kill();
|
|
||||||
return;
|
return;
|
||||||
}
|
QByteArray psOut = runPs();
|
||||||
QByteArray which = psProc.readAll();
|
m_processPID = extractPid(m_packageName, psOut);
|
||||||
if (which.startsWith("busybox")) {
|
if (m_processPID == -1)
|
||||||
psCmd = QLatin1String("ps -w");
|
|
||||||
psPidRx = QLatin1String("(\\d+)");
|
|
||||||
}
|
|
||||||
|
|
||||||
psProc.start(AndroidConfigurations::instance().adbToolPath().toString(),
|
|
||||||
AndroidDeviceInfo::adbSelector(m_deviceSerialNumber)
|
|
||||||
<< QLatin1String("shell") << psCmd);
|
|
||||||
if (!psProc.waitForFinished(-1)) {
|
|
||||||
psProc.kill();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QRegExp rx(psPidRx);
|
|
||||||
qint64 pid = -1;
|
|
||||||
QList<QByteArray> procs = psProc.readAll().split('\n');
|
|
||||||
foreach (const QByteArray &proc, procs) {
|
|
||||||
if (proc.trimmed().endsWith(m_packageName.toLatin1())) {
|
|
||||||
if (rx.indexIn(QLatin1String(proc)) > -1) {
|
|
||||||
pid = rx.cap(1).toLongLong();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-1 != m_processPID && pid == -1) {
|
|
||||||
m_processPID = -1;
|
|
||||||
emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName));
|
emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName));
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
m_processPID = pid;
|
|
||||||
|
|
||||||
if (!m_useCppDebugger)
|
void AndroidRunner::forceStop()
|
||||||
return;
|
{
|
||||||
m_gdbserverPID = -1;
|
QProcess proc;
|
||||||
foreach (const QByteArray &proc, procs) {
|
proc.start(m_adb, selector() << _("shell") << _("am") << _("force-stop"));
|
||||||
if (proc.trimmed().endsWith("gdbserver")) {
|
proc.waitForFinished();
|
||||||
if (rx.indexIn(QLatin1String(proc)) > -1) {
|
|
||||||
m_gdbserverPID = rx.cap(1).toLongLong();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::killPID()
|
void AndroidRunner::killPID()
|
||||||
{
|
{
|
||||||
checkPID(); //updates m_processPID and m_gdbserverPID
|
const QByteArray out = runPs();
|
||||||
for (int tries = 0; tries < 10 && (m_processPID != -1 || m_gdbserverPID != -1); ++tries) {
|
int from = 0;
|
||||||
if (m_processPID != -1) {
|
while (1) {
|
||||||
adbKill(m_processPID, m_deviceSerialNumber, 2000);
|
const int to = out.indexOf('\n', from);
|
||||||
adbKill(m_processPID, m_deviceSerialNumber, 2000, m_packageName);
|
if (to == -1)
|
||||||
|
break;
|
||||||
|
QString line = QString::fromUtf8(out.data() + from, to - from - 1);
|
||||||
|
if (line.endsWith(m_packageName) || line.endsWith(m_gdbserverPath)) {
|
||||||
|
int pid = extractPidFromChunk(out, from);
|
||||||
|
adbKill(pid);
|
||||||
}
|
}
|
||||||
|
from = to + 1;
|
||||||
if (m_gdbserverPID != -1) {
|
|
||||||
adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000);
|
|
||||||
adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000, m_packageName);
|
|
||||||
}
|
|
||||||
checkPID();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::start()
|
void AndroidRunner::start()
|
||||||
{
|
{
|
||||||
QtConcurrent::run(this,&AndroidRunner::asyncStart);
|
m_adbLogcatProcess.start(m_adb, selector() << _("logcat"));
|
||||||
|
m_wasStarted = false;
|
||||||
|
m_checkPIDTimer.start(1000); // check if the application is alive every 1 seconds
|
||||||
|
QtConcurrent::run(this, &AndroidRunner::asyncStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::asyncStart()
|
void AndroidRunner::asyncStart()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
m_processPID = -1;
|
forceStop();
|
||||||
killPID(); // kill any process with this name
|
killPID();
|
||||||
QString extraParams;
|
|
||||||
QProcess adbStarProc;
|
|
||||||
if (m_useCppDebugger) {
|
if (m_useCppDebugger) {
|
||||||
QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
|
// Remove pong file.
|
||||||
arguments << QLatin1String("forward") << QString::fromLatin1("tcp%1").arg(m_remoteGdbChannel)
|
QProcess adb;
|
||||||
<< QString::fromLatin1("localfilesystem:/data/data/%1/debug-socket").arg(m_packageName);
|
adb.start(m_adb, selector() << _("shell") << _("rm") << m_pongFile);
|
||||||
adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments);
|
adb.waitForFinished();
|
||||||
if (!adbStarProc.waitForStarted()) {
|
}
|
||||||
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adbStarProc.errorString()));
|
|
||||||
|
QStringList args = selector();
|
||||||
|
args << _("shell") << _("am") << _("start") << _("-n") << m_intentName;
|
||||||
|
|
||||||
|
if (m_useCppDebugger) {
|
||||||
|
QProcess adb;
|
||||||
|
adb.start(m_adb, selector() << _("forward")
|
||||||
|
<< QString::fromLatin1("tcp:%1").arg(m_localGdbServerPort)
|
||||||
|
<< _("localfilesystem:") + m_gdbserverSocket);
|
||||||
|
if (!adb.waitForStarted()) {
|
||||||
|
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adb.errorString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!adbStarProc.waitForFinished(-1)) {
|
if (!adb.waitForFinished(-1)) {
|
||||||
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports."));
|
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
extraParams = QLatin1String("-e native_debug true -e gdbserver_socket +debug-socket");
|
|
||||||
|
args << _("-e") << _("debug_ping") << _("true");
|
||||||
|
args << _("-e") << _("ping_file") << m_pingFile;
|
||||||
|
args << _("-e") << _("pong_file") << m_pongFile;
|
||||||
|
args << _("-e") << _("gdbserver_command") << m_gdbserverCommand;
|
||||||
|
args << _("-e") << _("gdbserver_socket") << m_gdbserverSocket;
|
||||||
}
|
}
|
||||||
if (m_useQmlDebugger) {
|
if (m_useQmlDebugger) {
|
||||||
QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
|
// currently forward to same port on device and host
|
||||||
QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
|
QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
|
||||||
arguments << QLatin1String("forward") << port << port; // currently forward to same port on device and host
|
QProcess adb;
|
||||||
adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments);
|
adb.start(m_adb, selector() << _("forward") << port << port);
|
||||||
if (!adbStarProc.waitForStarted()) {
|
if (!adb.waitForStarted()) {
|
||||||
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adbStarProc.errorString()));
|
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adb.errorString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!adbStarProc.waitForFinished(-1)) {
|
if (!adb.waitForFinished()) {
|
||||||
emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
|
emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
extraParams+=QString::fromLatin1(" -e qml_debug true -e qmljsdebugger port:%1")
|
args << _("-e") << _("qml_debug") << _("true");
|
||||||
.arg(m_qmlPort);
|
args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1").arg(m_qmlPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_useLocalQtLibs) {
|
if (m_useLocalQtLibs) {
|
||||||
extraParams += QLatin1String(" -e use_local_qt_libs true");
|
args << _("-e") << _("use_local_qt_libs") << _("true");
|
||||||
extraParams += QLatin1String(" -e libs_prefix /data/local/tmp/qt/");
|
args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/");
|
||||||
extraParams += QLatin1String(" -e load_local_libs ") + m_localLibs;
|
args << _("-e") << _("load_local_libs") << m_localLibs;
|
||||||
extraParams += QLatin1String(" -e load_local_jars ") + m_localJars;
|
args << _("-e") << _("load_local_jars") << m_localJars;
|
||||||
if (!m_localJarsInitClasses.isEmpty())
|
if (!m_localJarsInitClasses.isEmpty())
|
||||||
extraParams += QLatin1String(" -e static_init_classes ") + m_localJarsInitClasses;
|
args << _("-e") << _("static_init_classes") << m_localJarsInitClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
extraParams = extraParams.trimmed();
|
QProcess adb;
|
||||||
QStringList arguments = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
|
adb.start(m_adb, args);
|
||||||
arguments << QLatin1String("shell") << QLatin1String("am")
|
if (!adb.waitForStarted()) {
|
||||||
<< QLatin1String("start") << QLatin1String("-n") << m_intentName;
|
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString()));
|
||||||
|
|
||||||
if (extraParams.length())
|
|
||||||
arguments << extraParams.split(QLatin1Char(' '));
|
|
||||||
|
|
||||||
adbStarProc.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments);
|
|
||||||
if (!adbStarProc.waitForStarted()) {
|
|
||||||
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adbStarProc.errorString()));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!adbStarProc.waitForFinished(-1)) {
|
if (!adb.waitForFinished(-1)) {
|
||||||
adbStarProc.terminate();
|
adb.terminate();
|
||||||
emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
|
emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QTime startTime = QTime::currentTime();
|
|
||||||
while (m_processPID == -1 && startTime.secsTo(QTime::currentTime()) < 10) { // wait up to 10 seconds for application to start
|
if (m_useCppDebugger || m_useQmlDebugger) {
|
||||||
checkPID();
|
|
||||||
|
// Handling ping.
|
||||||
|
for (int i = 0; ; ++i) {
|
||||||
|
QTemporaryFile tmp(_("pingpong"));
|
||||||
|
tmp.open();
|
||||||
|
tmp.close();
|
||||||
|
|
||||||
|
QProcess process;
|
||||||
|
process.start(m_adb, selector() << _("pull") << m_pingFile << tmp.fileName());
|
||||||
|
process.waitForFinished();
|
||||||
|
|
||||||
|
QFile res(tmp.fileName());
|
||||||
|
const bool doBreak = res.size();
|
||||||
|
res.remove();
|
||||||
|
if (doBreak)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i == 20) {
|
||||||
|
emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
qDebug() << "WAITING FOR " << tmp.fileName();
|
||||||
|
QThread::msleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray psOut = runPs();
|
||||||
|
m_processPID = extractPid(m_packageName, psOut);
|
||||||
|
|
||||||
if (m_processPID == -1) {
|
if (m_processPID == -1) {
|
||||||
emit remoteProcessFinished(tr("Cannot find %1 process.").arg(m_packageName));
|
emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_useCppDebugger) {
|
m_wasStarted = true;
|
||||||
startTime = QTime::currentTime();
|
if (m_useCppDebugger || m_useQmlDebugger) {
|
||||||
while (m_gdbserverPID == -1 && startTime.secsTo(QTime::currentTime()) < 25) { // wait up to 25 seconds to connect
|
// This will be funneled to the engine to actually start and attach
|
||||||
checkPID();
|
// gdb. Afterwards this ends up in handleGdbRunning() below.
|
||||||
|
QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort);
|
||||||
|
emit remoteServerRunning(serverChannel, m_processPID);
|
||||||
|
} else {
|
||||||
|
// Start without debugging.
|
||||||
|
emit remoteProcessStarted(-1, -1);
|
||||||
}
|
}
|
||||||
msleep(200); // give gdbserver more time to start
|
|
||||||
}
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "startLogcat", Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::startLogcat()
|
void AndroidRunner::handleGdbRunning()
|
||||||
{
|
{
|
||||||
m_checkPIDTimer.start(1000); // check if the application is alive every 1 seconds
|
QTemporaryFile tmp(_("pingpong"));
|
||||||
m_adbLogcatProcess.start(AndroidConfigurations::instance().adbToolPath().toString(),
|
tmp.open();
|
||||||
AndroidDeviceInfo::adbSelector(m_deviceSerialNumber)
|
|
||||||
<< QLatin1String("logcat"));
|
QProcess process;
|
||||||
emit remoteProcessStarted(5039);
|
process.start(m_adb, selector() << _("push") << tmp.fileName() << m_pongFile);
|
||||||
|
process.waitForFinished();
|
||||||
|
|
||||||
|
QTC_CHECK(m_processPID != -1);
|
||||||
|
emit remoteProcessStarted(m_localGdbServerPort, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::stop()
|
void AndroidRunner::stop()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
m_checkPIDTimer.stop();
|
m_checkPIDTimer.stop();
|
||||||
if (m_processPID == -1) {
|
if (m_processPID != -1) {
|
||||||
m_adbLogcatProcess.kill();
|
|
||||||
return; // don't emit another signal
|
|
||||||
}
|
|
||||||
killPID();
|
killPID();
|
||||||
|
emit remoteProcessFinished(tr("\n\n'%1' terminated.").arg(m_packageName));
|
||||||
|
}
|
||||||
|
//QObject::disconnect(&m_adbLogcatProcess, 0, this, 0);
|
||||||
m_adbLogcatProcess.kill();
|
m_adbLogcatProcess.kill();
|
||||||
m_adbLogcatProcess.waitForFinished(-1);
|
m_adbLogcatProcess.waitForFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::logcatReadStandardError()
|
void AndroidRunner::logcatReadStandardError()
|
||||||
@@ -288,20 +350,21 @@ void AndroidRunner::logcatReadStandardOutput()
|
|||||||
m_logcat = line;
|
m_logcat = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunner::adbKill(qint64 pid, const QString &device, int timeout, const QString &runAsPackageName)
|
void AndroidRunner::adbKill(qint64 pid)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
QProcess process;
|
QProcess process;
|
||||||
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
|
process.start(m_adb, selector() << _("shell")
|
||||||
|
<< _("kill") << QLatin1String("-9") << QString::number(pid));
|
||||||
arguments << QLatin1String("shell");
|
process.waitForFinished();
|
||||||
if (runAsPackageName.size())
|
}
|
||||||
arguments << QLatin1String("run-as") << runAsPackageName;
|
{
|
||||||
arguments << QLatin1String("kill") << QLatin1String("-9");
|
QProcess process;
|
||||||
arguments << QString::number(pid);
|
process.start(m_adb, selector() << _("shell")
|
||||||
|
<< _("run-as") << m_packageName
|
||||||
process.start(AndroidConfigurations::instance().adbToolPath().toString(), arguments);
|
<< _("kill") << QLatin1String("-9") << QString::number(pid));
|
||||||
if (!process.waitForFinished(timeout))
|
process.waitForFinished();
|
||||||
process.kill();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AndroidRunner::displayName() const
|
QString AndroidRunner::displayName() const
|
||||||
|
@@ -56,9 +56,11 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
void handleGdbRunning();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void remoteProcessStarted(int gdbServerPort = -1, int qmlPort = -1);
|
void remoteServerRunning(const QByteArray &serverChannel, int pid);
|
||||||
|
void remoteProcessStarted(int gdbServerPort, int qmlPort);
|
||||||
void remoteProcessFinished(const QString &errString = QString());
|
void remoteProcessFinished(const QString &errString = QString());
|
||||||
|
|
||||||
void remoteOutput(const QByteArray &output);
|
void remoteOutput(const QByteArray &output);
|
||||||
@@ -69,29 +71,41 @@ private slots:
|
|||||||
void checkPID();
|
void checkPID();
|
||||||
void logcatReadStandardError();
|
void logcatReadStandardError();
|
||||||
void logcatReadStandardOutput();
|
void logcatReadStandardOutput();
|
||||||
void startLogcat();
|
|
||||||
void asyncStart();
|
void asyncStart();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void adbKill(qint64 pid, const QString &device, int timeout = 2000, const QString &runAsPackageName = QString());
|
void adbKill(qint64 pid);
|
||||||
|
QStringList selector() const { return m_selector; }
|
||||||
|
void forceStop();
|
||||||
|
QByteArray runPs();
|
||||||
|
void findPs();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QProcess m_adbLogcatProcess;
|
QProcess m_adbLogcatProcess;
|
||||||
|
QTimer m_checkPIDTimer;
|
||||||
|
bool m_wasStarted;
|
||||||
|
|
||||||
QByteArray m_logcat;
|
QByteArray m_logcat;
|
||||||
QString m_intentName;
|
QString m_intentName;
|
||||||
QString m_packageName;
|
QString m_packageName;
|
||||||
QString m_deviceSerialNumber;
|
QString m_deviceSerialNumber;
|
||||||
qint64 m_processPID;
|
qint64 m_processPID;
|
||||||
qint64 m_gdbserverPID;
|
|
||||||
QTimer m_checkPIDTimer;
|
|
||||||
bool m_useCppDebugger;
|
bool m_useCppDebugger;
|
||||||
bool m_useQmlDebugger;
|
bool m_useQmlDebugger;
|
||||||
QString m_remoteGdbChannel;
|
ushort m_localGdbServerPort; // Local end of forwarded debug socket.
|
||||||
uint m_qmlPort;
|
uint m_qmlPort;
|
||||||
bool m_useLocalQtLibs;
|
bool m_useLocalQtLibs;
|
||||||
|
QString m_pingFile;
|
||||||
|
QString m_pongFile;
|
||||||
|
QString m_gdbserverPath;
|
||||||
|
QString m_gdbserverCommand;
|
||||||
|
QString m_gdbserverSocket;
|
||||||
QString m_localLibs;
|
QString m_localLibs;
|
||||||
QString m_localJars;
|
QString m_localJars;
|
||||||
QString m_localJarsInitClasses;
|
QString m_localJarsInitClasses;
|
||||||
|
QString m_adb;
|
||||||
|
bool m_isBusyBox;
|
||||||
|
QStringList m_selector;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -879,6 +879,11 @@ void DebuggerEngine::notifyEngineRequestRemoteSetup()
|
|||||||
emit requestRemoteSetup();
|
emit requestRemoteSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebuggerEngine::notifyEngineRemoteServerRunning(const QByteArray &, int /*pid*/)
|
||||||
|
{
|
||||||
|
showMessage(_("NOTE: REMOTE SERVER RUNNING IN MULTIMODE"));
|
||||||
|
}
|
||||||
|
|
||||||
void DebuggerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
|
void DebuggerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
|
||||||
{
|
{
|
||||||
showMessage(_("NOTE: REMOTE SETUP DONE: GDB SERVER PORT: %1 QML PORT %2")
|
showMessage(_("NOTE: REMOTE SETUP DONE: GDB SERVER PORT: %1 QML PORT %2")
|
||||||
|
@@ -299,6 +299,7 @@ protected:
|
|||||||
|
|
||||||
virtual void notifyEngineRequestRemoteSetup();
|
virtual void notifyEngineRequestRemoteSetup();
|
||||||
public:
|
public:
|
||||||
|
virtual void notifyEngineRemoteServerRunning(const QByteArray &, int pid);
|
||||||
virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
|
virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
|
||||||
virtual void notifyEngineRemoteSetupFailed(const QString &message);
|
virtual void notifyEngineRemoteSetupFailed(const QString &message);
|
||||||
|
|
||||||
|
@@ -1267,6 +1267,7 @@ void GdbEngine::handleResultRecord(GdbResponse *response)
|
|||||||
|
|
||||||
bool GdbEngine::acceptsDebuggerCommands() const
|
bool GdbEngine::acceptsDebuggerCommands() const
|
||||||
{
|
{
|
||||||
|
return true;
|
||||||
return state() == InferiorStopOk
|
return state() == InferiorStopOk
|
||||||
|| state() == InferiorUnrunnable;
|
|| state() == InferiorUnrunnable;
|
||||||
}
|
}
|
||||||
@@ -3740,7 +3741,7 @@ void GdbEngine::handleThreadInfo(const GdbResponse &response)
|
|||||||
selectThread(other);
|
selectThread(other);
|
||||||
}
|
}
|
||||||
updateViews(); // Adjust Threads combobox.
|
updateViews(); // Adjust Threads combobox.
|
||||||
if (m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) {
|
if (false && m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) {
|
||||||
postCommand("threadnames " +
|
postCommand("threadnames " +
|
||||||
debuggerCore()->action(MaximalStackDepth)->value().toByteArray(),
|
debuggerCore()->action(MaximalStackDepth)->value().toByteArray(),
|
||||||
Discardable, CB(handleThreadNames));
|
Discardable, CB(handleThreadNames));
|
||||||
|
@@ -60,6 +60,8 @@ namespace Internal {
|
|||||||
GdbRemoteServerEngine::GdbRemoteServerEngine(const DebuggerStartParameters &startParameters)
|
GdbRemoteServerEngine::GdbRemoteServerEngine(const DebuggerStartParameters &startParameters)
|
||||||
: GdbEngine(startParameters)
|
: GdbEngine(startParameters)
|
||||||
{
|
{
|
||||||
|
m_isMulti = false;
|
||||||
|
m_targetPid = -1;
|
||||||
connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
|
connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
|
||||||
SLOT(uploadProcError(QProcess::ProcessError)));
|
SLOT(uploadProcError(QProcess::ProcessError)));
|
||||||
connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
|
connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
|
||||||
@@ -261,13 +263,8 @@ void GdbRemoteServerEngine::handleFileExecAndSymbols(const GdbResponse &response
|
|||||||
|
|
||||||
void GdbRemoteServerEngine::callTargetRemote()
|
void GdbRemoteServerEngine::callTargetRemote()
|
||||||
{
|
{
|
||||||
//m_breakHandler->clearBreakMarkers();
|
QByteArray rawChannel = startParameters().remoteChannel.toLatin1();
|
||||||
|
QByteArray channel = rawChannel;
|
||||||
// "target remote" does three things:
|
|
||||||
// (1) connects to the gdb server
|
|
||||||
// (2) starts the remote application
|
|
||||||
// (3) stops the remote application (early, e.g. in the dynamic linker)
|
|
||||||
QByteArray channel = startParameters().remoteChannel.toLatin1();
|
|
||||||
|
|
||||||
// Don't touch channels with explicitly set protocols.
|
// Don't touch channels with explicitly set protocols.
|
||||||
if (!channel.startsWith("tcp:") && !channel.startsWith("udp:")
|
if (!channel.startsWith("tcp:") && !channel.startsWith("udp:")
|
||||||
@@ -283,14 +280,16 @@ void GdbRemoteServerEngine::callTargetRemote()
|
|||||||
|
|
||||||
if (m_isQnxGdb)
|
if (m_isQnxGdb)
|
||||||
postCommand("target qnx " + channel, CB(handleTargetQnx));
|
postCommand("target qnx " + channel, CB(handleTargetQnx));
|
||||||
|
else if (m_isMulti)
|
||||||
|
postCommand("target extended-remote " + m_serverChannel, CB(handleTargetExtendedRemote));
|
||||||
else
|
else
|
||||||
postCommand("target remote " + channel, CB(handleTargetRemote));
|
postCommand("target remote " + channel, CB(handleTargetRemote), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &record)
|
void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &response)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
||||||
if (record.resultClass == GdbResultDone) {
|
if (response.resultClass == GdbResultDone) {
|
||||||
// gdb server will stop the remote application itself.
|
// gdb server will stop the remote application itself.
|
||||||
showMessage(_("INFERIOR STARTED"));
|
showMessage(_("INFERIOR STARTED"));
|
||||||
showMessage(msgAttachedToStoppedInferior(), StatusBar);
|
showMessage(msgAttachedToStoppedInferior(), StatusBar);
|
||||||
@@ -303,11 +302,50 @@ void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &record)
|
|||||||
} else {
|
} else {
|
||||||
// 16^error,msg="hd:5555: Connection timed out."
|
// 16^error,msg="hd:5555: Connection timed out."
|
||||||
QString msg = msgConnectRemoteServerFailed(
|
QString msg = msgConnectRemoteServerFailed(
|
||||||
QString::fromLocal8Bit(record.data.findChild("msg").data()));
|
QString::fromLocal8Bit(response.data.findChild("msg").data()));
|
||||||
notifyInferiorSetupFailed(msg);
|
notifyInferiorSetupFailed(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GdbRemoteServerEngine::handleTargetExtendedRemote(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
||||||
|
if (response.resultClass == GdbResultDone) {
|
||||||
|
// gdb server will stop the remote application itself.
|
||||||
|
showMessage(_("ATTACHED TO GDB SERVER STARTED"));
|
||||||
|
showMessage(msgAttachedToStoppedInferior(), StatusBar);
|
||||||
|
QString postAttachCommands = debuggerCore()->stringSetting(GdbPostAttachCommands);
|
||||||
|
if (!postAttachCommands.isEmpty()) {
|
||||||
|
foreach (const QString &cmd, postAttachCommands.split(QLatin1Char('\n')))
|
||||||
|
postCommand(cmd.toLatin1());
|
||||||
|
}
|
||||||
|
postCommand("attach " + QByteArray::number(m_targetPid), CB(handleTargetExtendedAttach));
|
||||||
|
} else {
|
||||||
|
QString msg = msgConnectRemoteServerFailed(
|
||||||
|
QString::fromLocal8Bit(response.data.findChild("msg").data()));
|
||||||
|
notifyInferiorSetupFailed(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRemoteServerEngine::handleTargetExtendedAttach(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
|
||||||
|
if (response.resultClass == GdbResultDone) {
|
||||||
|
// gdb server will stop the remote application itself.
|
||||||
|
handleInferiorPrepared();
|
||||||
|
} else {
|
||||||
|
QString msg = msgConnectRemoteServerFailed(
|
||||||
|
QString::fromLocal8Bit(response.data.findChild("msg").data()));
|
||||||
|
notifyInferiorSetupFailed(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRemoteServerEngine::notifyInferiorSetupOk()
|
||||||
|
{
|
||||||
|
emit aboutToNotifyInferiorSetupOk();
|
||||||
|
GdbEngine::notifyInferiorSetupOk();
|
||||||
|
}
|
||||||
|
|
||||||
void GdbRemoteServerEngine::handleTargetQnx(const GdbResponse &response)
|
void GdbRemoteServerEngine::handleTargetQnx(const GdbResponse &response)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(m_isQnxGdb, qDebug() << m_isQnxGdb);
|
QTC_ASSERT(m_isQnxGdb, qDebug() << m_isQnxGdb);
|
||||||
@@ -417,11 +455,24 @@ void GdbRemoteServerEngine::shutdownEngine()
|
|||||||
notifyAdapterShutdownOk();
|
notifyAdapterShutdownOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GdbRemoteServerEngine::notifyEngineRemoteServerRunning
|
||||||
|
(const QByteArray &serverChannel, int inferiorPid)
|
||||||
|
{
|
||||||
|
showMessage(_("NOTE: REMOTE SERVER RUNNING IN MULTIMODE"));
|
||||||
|
m_isMulti = true;
|
||||||
|
m_targetPid = inferiorPid;
|
||||||
|
m_serverChannel = serverChannel;
|
||||||
|
startGdb();
|
||||||
|
}
|
||||||
|
|
||||||
void GdbRemoteServerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
|
void GdbRemoteServerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
||||||
DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
|
DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
|
||||||
|
|
||||||
|
if (m_isMulti) {
|
||||||
|
// Has been done in notifyEngineRemoteServerRunning
|
||||||
|
} else {
|
||||||
if (qmlPort != -1)
|
if (qmlPort != -1)
|
||||||
startParameters().qmlServerPort = qmlPort;
|
startParameters().qmlServerPort = qmlPort;
|
||||||
if (gdbServerPort != -1) {
|
if (gdbServerPort != -1) {
|
||||||
@@ -433,6 +484,7 @@ void GdbRemoteServerEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int q
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
startGdb();
|
startGdb();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbRemoteServerEngine::notifyEngineRemoteSetupFailed(const QString &reason)
|
void GdbRemoteServerEngine::notifyEngineRemoteSetupFailed(const QString &reason)
|
||||||
|
@@ -70,20 +70,25 @@ signals:
|
|||||||
* a server start script should be used, but none is given.
|
* a server start script should be used, but none is given.
|
||||||
*/
|
*/
|
||||||
void requestSetup();
|
void requestSetup();
|
||||||
|
void aboutToNotifyInferiorSetupOk();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_SLOT void readUploadStandardOutput();
|
Q_SLOT void readUploadStandardOutput();
|
||||||
Q_SLOT void readUploadStandardError();
|
Q_SLOT void readUploadStandardError();
|
||||||
Q_SLOT void uploadProcError(QProcess::ProcessError error);
|
Q_SLOT void uploadProcError(QProcess::ProcessError error);
|
||||||
Q_SLOT void uploadProcFinished();
|
Q_SLOT void uploadProcFinished();
|
||||||
|
Q_SLOT void callTargetRemote();
|
||||||
|
|
||||||
virtual void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
|
void notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort);
|
||||||
virtual void notifyEngineRemoteSetupFailed(const QString &reason);
|
void notifyEngineRemoteSetupFailed(const QString &reason);
|
||||||
|
void notifyEngineRemoteServerRunning(const QByteArray &serverChannel, int inferiorPid);
|
||||||
|
void notifyInferiorSetupOk();
|
||||||
|
|
||||||
void handleSetTargetAsync(const GdbResponse &response);
|
void handleSetTargetAsync(const GdbResponse &response);
|
||||||
void handleFileExecAndSymbols(const GdbResponse &response);
|
void handleFileExecAndSymbols(const GdbResponse &response);
|
||||||
void callTargetRemote();
|
|
||||||
void handleTargetRemote(const GdbResponse &response);
|
void handleTargetRemote(const GdbResponse &response);
|
||||||
|
void handleTargetExtendedRemote(const GdbResponse &response);
|
||||||
|
void handleTargetExtendedAttach(const GdbResponse &response);
|
||||||
void handleTargetQnx(const GdbResponse &response);
|
void handleTargetQnx(const GdbResponse &response);
|
||||||
void handleAttach(const GdbResponse &response);
|
void handleAttach(const GdbResponse &response);
|
||||||
void handleInterruptInferior(const GdbResponse &response);
|
void handleInterruptInferior(const GdbResponse &response);
|
||||||
@@ -91,6 +96,9 @@ private:
|
|||||||
|
|
||||||
QProcess m_uploadProc;
|
QProcess m_uploadProc;
|
||||||
LocalGdbProcess m_gdbProc;
|
LocalGdbProcess m_gdbProc;
|
||||||
|
bool m_isMulti;
|
||||||
|
int m_targetPid;
|
||||||
|
QByteArray m_serverChannel;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
Reference in New Issue
Block a user