File indexing completed on 2024-04-28 04:38:34
0001 /* 0002 SPDX-FileCopyrightText: 1999 John Birch <jbb@kdevelop.org > 0003 SPDX-FileCopyrightText: 2007 Vladimir Prus <ghost@cs.msu.su> 0004 SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "midebugger.h" 0010 0011 #include "debuglog.h" 0012 #include "mi/micommand.h" 0013 0014 #include <interfaces/icore.h> 0015 #include <interfaces/iuicontroller.h> 0016 #include <sublime/message.h> 0017 0018 #include <KLocalizedString> 0019 #include <KMessageBox> 0020 0021 #include <QApplication> 0022 #include <QString> 0023 #include <QStringList> 0024 0025 #include <csignal> 0026 0027 #include <memory> 0028 #include <stdexcept> 0029 #include <sstream> 0030 0031 #ifdef Q_OS_WIN 0032 #include <Windows.h> 0033 #endif 0034 0035 // #define DEBUG_NO_TRY //to get a backtrace to where the exception was thrown 0036 0037 using namespace KDevMI; 0038 using namespace KDevMI::MI; 0039 0040 MIDebugger::MIDebugger(QObject* parent) 0041 : QObject(parent) 0042 { 0043 m_process = new KProcess(this); 0044 m_process->setOutputChannelMode(KProcess::SeparateChannels); 0045 connect(m_process, &KProcess::readyReadStandardOutput, 0046 this, &MIDebugger::readyReadStandardOutput); 0047 connect(m_process, &KProcess::readyReadStandardError, 0048 this, &MIDebugger::readyReadStandardError); 0049 connect(m_process, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished), 0050 this, &MIDebugger::processFinished); 0051 connect(m_process, &QProcess::errorOccurred, 0052 this, &MIDebugger::processErrored); 0053 } 0054 0055 MIDebugger::~MIDebugger() 0056 { 0057 // prevent Qt warning: QProcess: Destroyed while process is still running. 0058 if (m_process && m_process->state() == QProcess::Running) { 0059 disconnect(m_process, &QProcess::errorOccurred, 0060 this, &MIDebugger::processErrored); 0061 m_process->kill(); 0062 m_process->waitForFinished(10); 0063 } 0064 } 0065 0066 void MIDebugger::execute(std::unique_ptr<MICommand> command) 0067 { 0068 m_currentCmd = std::move(command); 0069 QString commandText = m_currentCmd->cmdToSend(); 0070 0071 qCDebug(DEBUGGERCOMMON) << "SEND:" << commandText.trimmed(); 0072 0073 QByteArray commandUtf8 = commandText.toUtf8(); 0074 0075 m_process->write(commandUtf8); 0076 m_currentCmd->markAsSubmitted(); 0077 0078 QString prettyCmd = m_currentCmd->cmdToSend(); 0079 prettyCmd.remove(QRegExp(QStringLiteral("set prompt \032.\n"))); 0080 prettyCmd = QLatin1String("(gdb) ") + prettyCmd; 0081 0082 if (m_currentCmd->isUserCommand()) 0083 emit userCommandOutput(prettyCmd); 0084 else 0085 emit internalCommandOutput(prettyCmd); 0086 } 0087 0088 bool MIDebugger::isReady() const 0089 { 0090 return m_currentCmd == nullptr; 0091 } 0092 0093 void MIDebugger::interrupt() 0094 { 0095 #ifndef Q_OS_WIN 0096 int pid = m_process->processId(); 0097 if (pid != 0) { 0098 ::kill(pid, SIGINT); 0099 } 0100 #else 0101 SetConsoleCtrlHandler(nullptr, true); 0102 GenerateConsoleCtrlEvent(0, 0); 0103 SetConsoleCtrlHandler(nullptr, false); 0104 #endif 0105 } 0106 0107 MICommand* MIDebugger::currentCommand() const 0108 { 0109 return m_currentCmd.get(); 0110 } 0111 0112 void MIDebugger::kill() 0113 { 0114 m_process->kill(); 0115 } 0116 0117 void MIDebugger::readyReadStandardOutput() 0118 { 0119 auto* const core = KDevelop::ICore::self(); 0120 if (!core || !core->debugController()) { 0121 const auto nullObject = core ? QLatin1String("the debug controller") 0122 : QLatin1String("the KDevelop core"); 0123 qCDebug(DEBUGGERCOMMON).nospace().noquote() 0124 << "Cannot process standard output without " << nullObject 0125 << ". KDevelop must be exiting and " << nullObject << " already destroyed."; 0126 return; 0127 } 0128 0129 m_process->setReadChannel(QProcess::StandardOutput); 0130 0131 m_buffer += m_process->readAll(); 0132 for (;;) 0133 { 0134 /* In MI mode, all messages are exactly one line. 0135 See if we have any complete lines in the buffer. */ 0136 int i = m_buffer.indexOf('\n'); 0137 if (i == -1) 0138 break; 0139 QByteArray reply(m_buffer.left(i)); 0140 m_buffer.remove(0, i+1); 0141 0142 processLine(reply); 0143 } 0144 } 0145 0146 void MIDebugger::readyReadStandardError() 0147 { 0148 m_process->setReadChannel(QProcess::StandardError); 0149 emit debuggerInternalOutput(QString::fromUtf8(m_process->readAll())); 0150 } 0151 0152 void MIDebugger::processLine(const QByteArray& line) 0153 { 0154 if (line != "(gdb) ") { 0155 qCDebug(DEBUGGERCOMMON) << "Debugger output (pid =" << m_process->processId() << "): " << line; 0156 } 0157 0158 FileSymbol file; 0159 file.contents = line; 0160 0161 std::unique_ptr<MI::Record> r(m_parser.parse(&file)); 0162 0163 if (!r) 0164 { 0165 // simply ignore the invalid MI message because both gdb and lldb 0166 // sometimes produces invalid messages that can be safely ignored. 0167 qCDebug(DEBUGGERCOMMON) << "Invalid MI message:" << line; 0168 // We don't consider the current command done. 0169 // So, if a command results in unparseable reply, 0170 // we'll just wait for the "right" reply, which might 0171 // never come. However, marking the command as 0172 // done in this case is even more risky. 0173 // It's probably possible to get here if we're debugging 0174 // natively without PTY, though this is uncommon case. 0175 return; 0176 } 0177 0178 #ifndef DEBUG_NO_TRY 0179 try 0180 { 0181 #endif 0182 switch(r->kind) 0183 { 0184 case MI::Record::Result: { 0185 auto& result = static_cast<MI::ResultRecord&>(*r); 0186 0187 // it's still possible for the user to issue a MI command, 0188 // emit correct signal 0189 if (m_currentCmd && m_currentCmd->isUserCommand()) { 0190 emit userCommandOutput(QString::fromUtf8(line) + QLatin1Char('\n')); 0191 } else { 0192 emit internalCommandOutput(QString::fromUtf8(line) + QLatin1Char('\n')); 0193 } 0194 0195 // protect against wild replies that sometimes returned from gdb without a pending command 0196 if (!m_currentCmd) 0197 { 0198 qCWarning(DEBUGGERCOMMON) << "Received a result without a pending command"; 0199 throw std::runtime_error("Received a result without a pending command"); 0200 } 0201 else if (m_currentCmd->token() != result.token) 0202 { 0203 std::stringstream ss; 0204 ss << "Received a result with token not matching pending command. " 0205 << "Pending: " << m_currentCmd->token() << "Received: " << result.token; 0206 qCWarning(DEBUGGERCOMMON) << ss.str().c_str(); 0207 throw std::runtime_error(ss.str()); 0208 } 0209 0210 // GDB doc: "running" and "exit" are status codes equivalent to "done" 0211 if (result.reason == QLatin1String("done") || result.reason == QLatin1String("running") || result.reason == QLatin1String("exit")) 0212 { 0213 qCDebug(DEBUGGERCOMMON) << "Result token is" << result.token; 0214 m_currentCmd->markAsCompleted(); 0215 qCDebug(DEBUGGERCOMMON) << "Command successful, times " 0216 << m_currentCmd->totalProcessingTime() 0217 << m_currentCmd->queueTime() 0218 << m_currentCmd->gdbProcessingTime(); 0219 m_currentCmd->invokeHandler(result); 0220 } 0221 else if (result.reason == QLatin1String("error")) 0222 { 0223 qCDebug(DEBUGGERCOMMON) << "Handling error"; 0224 m_currentCmd->markAsCompleted(); 0225 qCDebug(DEBUGGERCOMMON) << "Command error, times" 0226 << m_currentCmd->totalProcessingTime() 0227 << m_currentCmd->queueTime() 0228 << m_currentCmd->gdbProcessingTime(); 0229 // Some commands want to handle errors themself. 0230 if (m_currentCmd->handlesError() && 0231 m_currentCmd->invokeHandler(result)) 0232 { 0233 qCDebug(DEBUGGERCOMMON) << "Invoked custom handler\n"; 0234 // Done, nothing more needed 0235 } 0236 else 0237 emit error(result); 0238 } 0239 else 0240 { 0241 qCDebug(DEBUGGERCOMMON) << "Unhandled result code: " << result.reason; 0242 } 0243 0244 m_currentCmd.reset(); 0245 emit ready(); 0246 break; 0247 } 0248 0249 case MI::Record::Async: { 0250 auto& async = static_cast<MI::AsyncRecord&>(*r); 0251 0252 switch (async.subkind) { 0253 case MI::AsyncRecord::Exec: { 0254 // Prefix '*'; asynchronous state changes of the target 0255 if (async.reason == QLatin1String("stopped")) 0256 { 0257 emit programStopped(async); 0258 } 0259 else if (async.reason == QLatin1String("running")) 0260 { 0261 emit programRunning(); 0262 } 0263 else 0264 { 0265 qCDebug(DEBUGGERCOMMON) << "Unhandled exec notification: " << async.reason; 0266 } 0267 break; 0268 } 0269 0270 case MI::AsyncRecord::Notify: { 0271 // Prefix '='; supplementary information that we should handle (new breakpoint etc.) 0272 emit notification(async); 0273 break; 0274 } 0275 0276 case MI::AsyncRecord::Status: { 0277 // Prefix '+'; GDB documentation: 0278 // On-going status information about progress of a slow operation; may be ignored 0279 break; 0280 } 0281 } 0282 break; 0283 } 0284 0285 case MI::Record::Stream: { 0286 0287 auto& s = static_cast<MI::StreamRecord&>(*r); 0288 0289 if (s.subkind == MI::StreamRecord::Target) { 0290 emit applicationOutput(s.message); 0291 } else if (s.subkind == MI::StreamRecord::Console) { 0292 if (m_currentCmd && m_currentCmd->isUserCommand()) 0293 emit userCommandOutput(s.message); 0294 else 0295 emit internalCommandOutput(s.message); 0296 0297 if (m_currentCmd) 0298 m_currentCmd->newOutput(s.message); 0299 } else { 0300 emit debuggerInternalOutput(s.message); 0301 } 0302 0303 emit streamRecord(s); 0304 0305 break; 0306 } 0307 0308 case MI::Record::Prompt: 0309 break; 0310 } 0311 #ifndef DEBUG_NO_TRY 0312 } 0313 catch(const std::exception& e) 0314 { 0315 KMessageBox::detailedError( 0316 qApp->activeWindow(), 0317 i18nc("<b>Internal debugger error</b>", 0318 "<p>The debugger component encountered an internal error while " 0319 "processing the reply from the debugger. Please submit a bug report. " 0320 "The debug session will now end to prevent potential crash"), 0321 i18n("The exception is: %1\n" 0322 "The MI response is: %2", QString::fromUtf8(e.what()), 0323 QString::fromLatin1(line)), 0324 i18nc("@title:window", "Internal Debugger Error")); 0325 emit exited(true, QString::fromUtf8(e.what())); 0326 } 0327 #endif 0328 } 0329 0330 void MIDebugger::processFinished(int exitCode, QProcess::ExitStatus exitStatus) 0331 { 0332 qCDebug(DEBUGGERCOMMON) << "Debugger FINISHED\n"; 0333 0334 bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit; 0335 emit userCommandOutput(QStringLiteral("Process exited\n")); 0336 emit exited(abnormal, i18n("Process exited")); 0337 } 0338 0339 void MIDebugger::processErrored(QProcess::ProcessError error) 0340 { 0341 qCWarning(DEBUGGERCOMMON) << "Debugger ERRORED" << error << m_process->errorString(); 0342 if(error == QProcess::FailedToStart) 0343 { 0344 const QString messageText = 0345 i18n("<b>Could not start debugger.</b>" 0346 "<p>Could not run '%1'. " 0347 "Make sure that the path name is specified correctly.", 0348 m_debuggerExecutable); 0349 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 0350 KDevelop::ICore::self()->uiController()->postMessage(message); 0351 0352 emit userCommandOutput(QStringLiteral("Process failed to start\n")); 0353 emit exited(true, i18n("Process failed to start")); 0354 0355 } else if (error == QProcess::Crashed) { 0356 KMessageBox::error( 0357 qApp->activeWindow(), 0358 i18n("<b>Debugger crashed.</b>" 0359 "<p>The debugger process '%1' crashed.<br>" 0360 "Because of that the debug session has to be ended.<br>" 0361 "Try to reproduce the crash without KDevelop and report a bug.<br>", 0362 m_debuggerExecutable), 0363 i18nc("@title:window", "Debugger Crashed")); 0364 0365 emit userCommandOutput(QStringLiteral("Process crashed\n")); 0366 emit exited(true, i18n("Process crashed")); 0367 } 0368 } 0369 0370 #include "moc_midebugger.cpp"