OSDN Git Service

1de02e050e1f280fa025d8349829e2b6b24050a6
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt-maemo / maemodebugsupport.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Creator.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 ** $QT_END_LICENSE$
32 **
33 ****************************************************************************/
34
35 #include "maemodebugsupport.h"
36
37 #include "maemodeployables.h"
38 #include "maemodeploystep.h"
39 #include "maemoglobal.h"
40 #include "maemosshrunner.h"
41 #include "maemousedportsgatherer.h"
42 #include "qt4maemotarget.h"
43
44 #include <utils/ssh/sftpchannel.h>
45 #include <debugger/debuggerplugin.h>
46 #include <debugger/debuggerstartparameters.h>
47 #include <debugger/debuggerrunner.h>
48 #include <debugger/debuggerengine.h>
49 #include <projectexplorer/abi.h>
50
51 #include <QtCore/QDir>
52 #include <QtCore/QFileInfo>
53
54 #define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state)
55
56 using namespace Utils;
57 using namespace Debugger;
58 using namespace ProjectExplorer;
59
60 namespace Qt4ProjectManager {
61 namespace Internal {
62
63 RunControl *MaemoDebugSupport::createDebugRunControl(MaemoRunConfiguration *runConfig)
64 {
65     DebuggerStartParameters params;
66     const MaemoDeviceConfig::ConstPtr &devConf = runConfig->deviceConfig();
67
68     const MaemoRunConfiguration::DebuggingType debuggingType
69         = runConfig->debuggingType();
70     if (debuggingType != MaemoRunConfiguration::DebugCppOnly) {
71         params.qmlServerAddress = runConfig->deviceConfig()->sshParameters().host;
72         params.qmlServerPort = -1;
73     }
74     if (debuggingType != MaemoRunConfiguration::DebugQmlOnly) {
75         params.processArgs = runConfig->arguments();
76         params.sysRoot = runConfig->sysRoot();
77         params.toolChainAbi = runConfig->abi();
78         params.dumperLibrary = runConfig->dumperLib();
79         params.remoteDumperLib = uploadDir(devConf).toUtf8() + '/'
80             + QFileInfo(runConfig->dumperLib()).fileName().toUtf8();
81         if (runConfig->useRemoteGdb()) {
82             params.startMode = StartRemoteGdb;
83             params.executable = runConfig->remoteExecutableFilePath();
84             params.debuggerCommand = MaemoGlobal::remoteCommandPrefix(runConfig->deviceConfig()->osVersion(),
85                 runConfig->remoteExecutableFilePath())
86                 + MaemoGlobal::remoteEnvironment(runConfig->userEnvironmentChanges())
87                 + QLatin1String(" /usr/bin/gdb");
88             params.connParams = devConf->sshParameters();
89             params.localMountDir = runConfig->localDirToMountForRemoteGdb();
90             params.remoteMountPoint
91                 = runConfig->remoteProjectSourcesMountPoint();
92             const QString execDirAbs
93                 = QDir::fromNativeSeparators(QFileInfo(runConfig->localExecutableFilePath()).path());
94             const QString execDirRel
95                 = QDir(params.localMountDir).relativeFilePath(execDirAbs);
96             params.remoteSourcesDir = QString(params.remoteMountPoint
97                 + QLatin1Char('/') + execDirRel).toUtf8();
98         } else {
99             params.startMode = AttachToRemote;
100             params.executable = runConfig->localExecutableFilePath();
101             params.debuggerCommand = runConfig->gdbCmd();
102             params.remoteChannel
103                 = devConf->sshParameters().host + QLatin1String(":-1");
104             params.useServerStartScript = true;
105             const AbstractQt4MaemoTarget::DebugArchitecture &debugArch
106                 = runConfig->maemoTarget()->debugArchitecture();
107             params.remoteArchitecture = debugArch.architecture;
108             params.gnuTarget = debugArch.gnuTarget;
109         }
110     } else {
111         params.startMode = AttachToRemote;
112     }
113     params.displayName = runConfig->displayName();
114
115     DebuggerRunControl * const runControl =
116         DebuggerPlugin::createDebugger(params, runConfig);
117     bool useGdb = params.startMode == StartRemoteGdb
118         && debuggingType != MaemoRunConfiguration::DebugQmlOnly;
119     MaemoDebugSupport *debugSupport =
120         new MaemoDebugSupport(runConfig, runControl->engine(), useGdb);
121     connect(runControl, SIGNAL(finished()),
122         debugSupport, SLOT(handleDebuggingFinished()));
123     return runControl;
124 }
125
126 MaemoDebugSupport::MaemoDebugSupport(MaemoRunConfiguration *runConfig,
127     DebuggerEngine *engine, bool useGdb)
128     : QObject(engine), m_engine(engine), m_runConfig(runConfig),
129       m_deviceConfig(m_runConfig->deviceConfig()),
130       m_runner(new MaemoSshRunner(this, runConfig, true)),
131       m_debuggingType(runConfig->debuggingType()),
132       m_dumperLib(runConfig->dumperLib()),
133       m_userEnvChanges(runConfig->userEnvironmentChanges()),
134       m_state(Inactive), m_gdbServerPort(-1), m_qmlPort(-1),
135       m_useGdb(useGdb)
136 {
137     connect(m_engine, SIGNAL(requestRemoteSetup()), this,
138         SLOT(handleAdapterSetupRequested()));
139 }
140
141 MaemoDebugSupport::~MaemoDebugSupport()
142 {
143     setState(Inactive);
144 }
145
146 void MaemoDebugSupport::showMessage(const QString &msg, int channel)
147 {
148     if (m_engine)
149         m_engine->showMessage(msg, channel);
150 }
151
152 void MaemoDebugSupport::handleAdapterSetupRequested()
153 {
154     ASSERT_STATE(Inactive);
155
156     setState(StartingRunner);
157     showMessage(tr("Preparing remote side ..."), AppStuff);
158     disconnect(m_runner, 0, this, 0);
159     connect(m_runner, SIGNAL(error(QString)), this,
160         SLOT(handleSshError(QString)));
161     connect(m_runner, SIGNAL(readyForExecution()), this,
162         SLOT(startExecution()));
163     connect(m_runner, SIGNAL(reportProgress(QString)), this,
164         SLOT(handleProgressReport(QString)));
165     m_runner->start();
166 }
167
168 void MaemoDebugSupport::handleSshError(const QString &error)
169 {
170     if (m_state == Debugging) {
171         showMessage(tr("SSH connection error: %1").arg(error),
172             AppError);
173         if (m_engine)
174             m_engine->notifyInferiorIll();
175     } else if (m_state != Inactive) {
176         handleAdapterSetupFailed(error);
177     }
178 }
179
180 void MaemoDebugSupport::startExecution()
181 {
182     if (m_state == Inactive)
183         return;
184
185     ASSERT_STATE(StartingRunner);
186
187     if (!useGdb() && m_debuggingType != MaemoRunConfiguration::DebugQmlOnly) {
188         if (!setPort(m_gdbServerPort))
189             return;
190     }
191     if (m_debuggingType != MaemoRunConfiguration::DebugCppOnly) {
192         if (!setPort(m_qmlPort))
193             return;
194     }
195
196     if (m_debuggingType != MaemoRunConfiguration::DebugQmlOnly
197             && !m_dumperLib.isEmpty()
198             && m_runConfig
199             && m_runConfig->deployStep()->currentlyNeedsDeployment(m_deviceConfig->sshParameters().host,
200                    MaemoDeployable(m_dumperLib, uploadDir(m_deviceConfig)))) {
201         setState(InitializingUploader);
202         m_uploader = m_runner->connection()->createSftpChannel();
203         connect(m_uploader.data(), SIGNAL(initialized()), this,
204                 SLOT(handleSftpChannelInitialized()));
205         connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
206                 SLOT(handleSftpChannelInitializationFailed(QString)));
207         connect(m_uploader.data(), SIGNAL(finished(Utils::SftpJobId, QString)),
208                 this, SLOT(handleSftpJobFinished(Utils::SftpJobId, QString)));
209         m_uploader->initialize();
210     } else {
211         setState(DumpersUploaded);
212         startDebugging();
213     }
214 }
215
216 void MaemoDebugSupport::handleSftpChannelInitialized()
217 {
218     if (m_state == Inactive)
219         return;
220
221     ASSERT_STATE(InitializingUploader);
222
223     const QString fileName = QFileInfo(m_dumperLib).fileName();
224     const QString remoteFilePath = uploadDir(m_deviceConfig)
225         + QLatin1Char('/') + fileName;
226     m_uploadJob = m_uploader->uploadFile(m_dumperLib, remoteFilePath,
227         SftpOverwriteExisting);
228     if (m_uploadJob == SftpInvalidJob) {
229         handleAdapterSetupFailed(tr("Upload failed: Could not open file '%1'")
230             .arg(m_dumperLib));
231     } else {
232         setState(UploadingDumpers);
233         showMessage(tr("Started uploading debugging helpers ('%1').")
234             .arg(m_dumperLib), AppStuff);
235     }
236 }
237
238 void MaemoDebugSupport::handleSftpChannelInitializationFailed(const QString &error)
239 {
240     if (m_state == Inactive)
241         return;
242     ASSERT_STATE(InitializingUploader);
243     handleAdapterSetupFailed(error);
244 }
245
246 void MaemoDebugSupport::handleSftpJobFinished(Utils::SftpJobId job,
247     const QString &error)
248 {
249     if (m_state == Inactive)
250         return;
251
252     ASSERT_STATE(UploadingDumpers);
253
254     if (job != m_uploadJob) {
255         qWarning("Warning: Unknown debugging helpers upload job %d finished.", job);
256         return;
257     }
258
259     if (!error.isEmpty()) {
260         handleAdapterSetupFailed(tr("Could not upload debugging helpers: %1.")
261             .arg(error));
262     } else {
263         setState(DumpersUploaded);
264         if (m_runConfig) {
265             m_runConfig->deployStep()->setDeployed(m_deviceConfig->sshParameters().host,
266                 MaemoDeployable(m_dumperLib, uploadDir(m_deviceConfig)));
267         }
268         showMessage(tr("Finished uploading debugging helpers."), AppStuff);
269         startDebugging();
270     }
271     m_uploadJob = SftpInvalidJob;
272 }
273
274 void MaemoDebugSupport::startDebugging()
275 {
276     ASSERT_STATE(DumpersUploaded);
277
278     if (useGdb()) {
279         handleAdapterSetupDone();
280     } else {
281         setState(StartingRemoteProcess);
282         m_gdbserverOutput.clear();
283         connect(m_runner, SIGNAL(remoteErrorOutput(QByteArray)), this,
284             SLOT(handleRemoteErrorOutput(QByteArray)));
285         connect(m_runner, SIGNAL(remoteOutput(QByteArray)), this,
286             SLOT(handleRemoteOutput(QByteArray)));
287         if (m_debuggingType == MaemoRunConfiguration::DebugQmlOnly) {
288             connect(m_runner, SIGNAL(remoteProcessStarted()),
289                 SLOT(handleRemoteProcessStarted()));
290         }
291         const QString &remoteExe = m_runner->remoteExecutable();
292         const QString cmdPrefix = MaemoGlobal::remoteCommandPrefix(m_deviceConfig->osVersion(),
293             remoteExe);
294         const QString env = MaemoGlobal::remoteEnvironment(m_userEnvChanges);
295         QString args = m_runner->arguments();
296         if (m_debuggingType != MaemoRunConfiguration::DebugCppOnly) {
297             args += QString(QLatin1String(" -qmljsdebugger=port:%1,block"))
298                 .arg(m_qmlPort);
299         }
300         const QString remoteCommandLine
301             = m_debuggingType == MaemoRunConfiguration::DebugQmlOnly
302                 ? QString::fromLocal8Bit("%1 %2 %3 %4").arg(cmdPrefix).arg(env)
303                       .arg(remoteExe).arg(args)
304                 : QString::fromLocal8Bit("%1 %2 gdbserver :%3 %4 %5")
305                       .arg(cmdPrefix).arg(env).arg(m_gdbServerPort)
306                       .arg(remoteExe).arg(args);
307         m_runner->startExecution(remoteCommandLine.toUtf8());
308     }
309 }
310
311 void MaemoDebugSupport::handleDebuggingFinished()
312 {
313     setState(Inactive);
314 }
315
316 void MaemoDebugSupport::handleRemoteOutput(const QByteArray &output)
317 {
318     ASSERT_STATE(QList<State>() << Inactive << Debugging);
319     showMessage(QString::fromUtf8(output), AppOutput);
320 }
321
322 void MaemoDebugSupport::handleRemoteErrorOutput(const QByteArray &output)
323 {
324     ASSERT_STATE(QList<State>() << Inactive << StartingRemoteProcess << Debugging);
325
326     if (!m_engine)
327         return;
328
329     showMessage(QString::fromUtf8(output), AppOutput);
330     if (m_state == StartingRemoteProcess
331             && m_debuggingType != MaemoRunConfiguration::DebugQmlOnly) {
332         m_gdbserverOutput += output;
333         if (m_gdbserverOutput.contains("Listening on port")) {
334             handleAdapterSetupDone();
335             m_gdbserverOutput.clear();
336         }
337     }
338 }
339
340 void MaemoDebugSupport::handleProgressReport(const QString &progressOutput)
341 {
342     showMessage(progressOutput, AppStuff);
343 }
344
345 void MaemoDebugSupport::handleAdapterSetupFailed(const QString &error)
346 {
347     setState(Inactive);
348     m_engine->handleRemoteSetupFailed(tr("Initial setup failed: %1").arg(error));
349 }
350
351 void MaemoDebugSupport::handleAdapterSetupDone()
352 {
353     setState(Debugging);
354     m_engine->handleRemoteSetupDone(m_gdbServerPort, m_qmlPort);
355 }
356
357 void MaemoDebugSupport::handleRemoteProcessStarted()
358 {
359     Q_ASSERT(m_debuggingType == MaemoRunConfiguration::DebugQmlOnly);
360     ASSERT_STATE(StartingRemoteProcess);
361     handleAdapterSetupDone();
362 }
363
364 void MaemoDebugSupport::setState(State newState)
365 {
366     if (m_state == newState)
367         return;
368     m_state = newState;
369     if (m_state == Inactive) {
370         if (m_uploader) {
371             disconnect(m_uploader.data(), 0, this, 0);
372             m_uploader->closeChannel();
373         }
374         m_runner->stop();
375     }
376 }
377
378 QString MaemoDebugSupport::uploadDir(const MaemoDeviceConfig::ConstPtr &devConf)
379 {
380     return MaemoGlobal::homeDirOnDevice(devConf->sshParameters().userName);
381 }
382
383 bool MaemoDebugSupport::useGdb() const
384 {
385     return m_useGdb;
386 }
387
388 bool MaemoDebugSupport::setPort(int &port)
389 {
390     port = m_runner->usedPortsGatherer()->getNextFreePort(m_runner->freePorts());
391     if (port == -1) {
392         handleAdapterSetupFailed(tr("Not enough free ports on device for debugging."));
393         return false;
394     }
395     return true;
396 }
397
398 } // namespace Internal
399 } // namespace Qt4ProjectManager