File indexing completed on 2024-04-28 04:38:34

0001 /*
0002     SPDX-FileCopyrightText: 2006 Vladimir Prus <ghost@cs.msu.su>
0003     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0004     SPDX-FileCopyrightText: 2009 Andreas Pakulat <apaku@gmx.de>
0005     SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "midebugjobs.h"
0011 
0012 #include "debuglog.h"
0013 #include "dialogs/selectcoredialog.h"
0014 #include "midebugsession.h"
0015 #include "midebuggerplugin.h"
0016 
0017 #include <execute/iexecuteplugin.h>
0018 #include <interfaces/icore.h>
0019 #include <interfaces/iproject.h>
0020 #include <interfaces/ilaunchconfiguration.h>
0021 #include <interfaces/iuicontroller.h>
0022 #include <outputview/outputmodel.h>
0023 #include <util/scopeddialog.h>
0024 
0025 #include <KConfigGroup>
0026 #include <KLocalizedString>
0027 #include <KParts/MainWindow>
0028 
0029 #include <QFileInfo>
0030 
0031 using namespace KDevMI;
0032 using namespace KDevelop;
0033 
0034 template<class JobBase>
0035 MIDebugJobBase<JobBase>::MIDebugJobBase(MIDebuggerPlugin* plugin, QObject* parent)
0036     : JobBase(parent)
0037 {
0038     Q_ASSERT(plugin);
0039 
0040     JobBase::setCapabilities(KJob::Killable);
0041 
0042     m_session = plugin->createSession();
0043     QObject::connect(m_session, &MIDebugSession::finished, this, &MIDebugJobBase::done);
0044 
0045     qCDebug(DEBUGGERCOMMON) << "created debug job" << this << "with" << m_session;
0046 }
0047 
0048 template<class JobBase>
0049 MIDebugJobBase<JobBase>::~MIDebugJobBase()
0050 {
0051     // Don't print m_session unconditionally, because it can be already destroyed.
0052     qCDebug(DEBUGGERCOMMON) << "destroying debug job" << this;
0053     // If this job is destroyed before it starts, m_session can be already destroyed even if this job is not finished.
0054     // For example, this occurs when the user starts debugging and immediately exits KDevelop.
0055     if (m_session && !JobBase::isFinished()) {
0056         qCDebug(DEBUGGERCOMMON) << "debug job destroyed before it finished, stopping debugger of" << m_session;
0057         m_session->stopDebugger();
0058     }
0059 }
0060 
0061 template<typename JobBase>
0062 void MIDebugJobBase<JobBase>::done()
0063 {
0064     qCDebug(DEBUGGERCOMMON) << "finishing debug job" << this << "with" << m_session;
0065     JobBase::emitResult();
0066 }
0067 
0068 template<typename JobBase>
0069 bool MIDebugJobBase<JobBase>::doKill()
0070 {
0071     qCDebug(DEBUGGERCOMMON) << "killing debug job" << this << "and stopping debugger of" << m_session;
0072     m_session->stopDebugger();
0073     return true;
0074 }
0075 
0076 MIDebugJob::MIDebugJob(MIDebuggerPlugin* p, ILaunchConfiguration* launchcfg,
0077                    IExecutePlugin* execute, QObject* parent)
0078     : MIDebugJobBase(p, parent)
0079     , m_launchcfg(launchcfg)
0080     , m_execute(execute)
0081 {
0082     connect(m_session, &MIDebugSession::inferiorStdoutLines, this, &MIDebugJob::stdoutReceived);
0083     connect(m_session, &MIDebugSession::inferiorStderrLines, this, &MIDebugJob::stderrReceived);
0084 
0085     if (launchcfg->project()) {
0086         setObjectName(i18nc("ProjectName: run configuration name", "%1: %2",
0087                             launchcfg->project()->name(), launchcfg->name()));
0088     } else {
0089         setObjectName(launchcfg->name());
0090     }
0091 }
0092 
0093 void MIDebugJob::start()
0094 {
0095     Q_ASSERT(m_execute);
0096 
0097     QString err;
0098 
0099     // check if the config is valid
0100     QString executable = m_execute->executable(m_launchcfg, err).toLocalFile();
0101     if (!err.isEmpty()) {
0102         finishWithError(InvalidExecutable, err);
0103         return;
0104     }
0105 
0106     if (!QFileInfo(executable).isExecutable()) {
0107         finishWithError(ExecutableIsNotExecutable, i18n("'%1' is not an executable", executable));
0108         return;
0109     }
0110 
0111     QStringList arguments = m_execute->arguments(m_launchcfg, err);
0112     if (!err.isEmpty()) {
0113         finishWithError(InvalidArguments, err);
0114         return;
0115     }
0116 
0117     setStandardToolView(IOutputView::DebugView);
0118     setBehaviours(IOutputView::Behaviours(IOutputView::AllowUserClose) | KDevelop::IOutputView::AutoScroll);
0119 
0120     auto model = new KDevelop::OutputModel;
0121     model->setFilteringStrategy(OutputModel::NativeAppErrorFilter);
0122     setModel(model);
0123     setTitle(m_launchcfg->name());
0124 
0125     KConfigGroup grp = m_launchcfg->config();
0126     QString startWith = grp.readEntry(Config::StartWithEntry, QStringLiteral("ApplicationOutput"));
0127     if (startWith == QLatin1String("ApplicationOutput")) {
0128         setVerbosity(Verbose);
0129     } else {
0130         setVerbosity(Silent);
0131     }
0132 
0133     startOutput();
0134 
0135     if (!m_session->startDebugging(m_launchcfg, m_execute)) {
0136         done();
0137     }
0138 }
0139 
0140 void MIDebugJob::stderrReceived(const QStringList& l)
0141 {
0142     if (OutputModel* m = model()) {
0143         m->appendLines(l);
0144     }
0145 }
0146 
0147 void MIDebugJob::stdoutReceived(const QStringList& l)
0148 {
0149     if (OutputModel* m = model()) {
0150         m->appendLines(l);
0151     }
0152 }
0153 
0154 void MIDebugJob::finishWithError(int errorCode, const QString& errorText)
0155 {
0156     qCDebug(DEBUGGERCOMMON) << "failing" << this << "and stopping debugger of" << m_session;
0157     m_session->stopDebugger();
0158     setError(errorCode);
0159     setErrorText(errorText);
0160     emitResult();
0161 }
0162 
0163 OutputModel* MIDebugJob::model()
0164 {
0165     return qobject_cast<OutputModel*>(OutputJob::model());
0166 }
0167 
0168 MIExamineCoreJob::MIExamineCoreJob(MIDebuggerPlugin *plugin, QObject *parent)
0169     : MIDebugJobBase(plugin, parent)
0170 {
0171     setObjectName(i18n("Debug core file"));
0172 }
0173 
0174 void MIExamineCoreJob::start()
0175 {
0176     ScopedDialog<SelectCoreDialog> dlg(ICore::self()->uiController()->activeMainWindow());
0177     if (dlg->exec() == QDialog::Rejected) {
0178         qCDebug(DEBUGGERCOMMON) << "Select Core File dialog rejected, finishing" << this << "and stopping debugger of"
0179                                 << m_session;
0180         m_session->stopDebugger();
0181         done();
0182         return;
0183     }
0184 
0185     if (!m_session->examineCoreFile(dlg->executableFile(), dlg->core())) {
0186         done();
0187     }
0188 }
0189 
0190 MIAttachProcessJob::MIAttachProcessJob(MIDebuggerPlugin *plugin, int pid, QObject *parent)
0191     : MIDebugJobBase(plugin, parent)
0192     , m_pid(pid)
0193 {
0194     setObjectName(i18n("Debug process %1", pid));
0195 }
0196 
0197 void MIAttachProcessJob::start()
0198 {
0199     if (!m_session->attachToProcess(m_pid)) {
0200         done();
0201     }
0202 }
0203 
0204 #include "moc_midebugjobs.cpp"