diff --git a/plugins/debuggercommon/midebugger.cpp b/plugins/debuggercommon/midebugger.cpp index 6571f2fcec..dce505ee28 100644 --- a/plugins/debuggercommon/midebugger.cpp +++ b/plugins/debuggercommon/midebugger.cpp @@ -1,365 +1,365 @@ /* * Low level GDB interface. * * Copyright 1999 John Birch * Copyright 2007 Vladimir Prus * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "midebugger.h" #include "debuglog.h" #include "mi/micommand.h" #include #include #include #include #include #include #include #include #include // #define DEBUG_NO_TRY //to get a backtrace to where the exception was thrown using namespace KDevMI; using namespace KDevMI::MI; MIDebugger::MIDebugger(QObject* parent) : QObject(parent) , m_process(nullptr) , m_currentCmd(nullptr) { m_process = new KProcess(this); m_process->setOutputChannelMode(KProcess::SeparateChannels); connect(m_process, &KProcess::readyReadStandardOutput, this, &MIDebugger::readyReadStandardOutput); connect(m_process, &KProcess::readyReadStandardError, this, &MIDebugger::readyReadStandardError); connect(m_process, static_cast(&KProcess::finished), this, &MIDebugger::processFinished); connect(m_process, static_cast(&KProcess::error), this, &MIDebugger::processErrored); } MIDebugger::~MIDebugger() { // prevent Qt warning: QProcess: Destroyed while process is still running. if (m_process && m_process->state() == QProcess::Running) { disconnect(m_process, static_cast(&KProcess::error), this, &MIDebugger::processErrored); m_process->kill(); m_process->waitForFinished(10); } } void MIDebugger::execute(MICommand* command) { m_currentCmd = command; QString commandText = m_currentCmd->cmdToSend(); qCDebug(DEBUGGERCOMMON) << "SEND:" << commandText.trimmed(); QByteArray commandUtf8 = commandText.toUtf8(); m_process->write(commandUtf8, commandUtf8.length()); command->markAsSubmitted(); QString prettyCmd = m_currentCmd->cmdToSend(); prettyCmd.remove( QRegExp("set prompt \032.\n") ); prettyCmd = "(gdb) " + prettyCmd; if (m_currentCmd->isUserCommand()) emit userCommandOutput(prettyCmd); else emit internalCommandOutput(prettyCmd); } bool MIDebugger::isReady() const { return m_currentCmd == nullptr; } void MIDebugger::interrupt() { //TODO:win32 Porting needed int pid = m_process->pid(); if (pid != 0) { ::kill(pid, SIGINT); } } MICommand* MIDebugger::currentCommand() const { return m_currentCmd; } void MIDebugger::kill() { m_process->kill(); } void MIDebugger::readyReadStandardOutput() { m_process->setReadChannel(QProcess::StandardOutput); m_buffer += m_process->readAll(); for (;;) { /* In MI mode, all messages are exactly one line. See if we have any complete lines in the buffer. */ int i = m_buffer.indexOf('\n'); if (i == -1) break; QByteArray reply(m_buffer.left(i)); m_buffer = m_buffer.mid(i+1); processLine(reply); } } void MIDebugger::readyReadStandardError() { m_process->setReadChannel(QProcess::StandardError); emit debuggerInternalOutput(QString::fromUtf8(m_process->readAll())); } void MIDebugger::processLine(const QByteArray& line) { qCDebug(DEBUGGERCOMMON) << "Debugger (" << m_process->pid() <<") output: " << line; FileSymbol file; file.contents = line; std::unique_ptr r(m_parser.parse(&file)); if (!r) { // simply ignore the invalid MI message because both gdb and lldb // sometimes produces invalid messages that can be safely ignored. qCDebug(DEBUGGERCOMMON) << "Invalid MI message:" << line; // We don't consider the current command done. // So, if a command results in unparseable reply, // we'll just wait for the "right" reply, which might // never come. However, marking the command as // done in this case is even more risky. // It's probably possible to get here if we're debugging // natively without PTY, though this is uncommon case. return; } #ifndef DEBUG_NO_TRY try { #endif switch(r->kind) { case MI::Record::Result: { MI::ResultRecord& result = static_cast(*r); // it's still possible for the user to issue a MI command, // emit correct signal if (m_currentCmd && m_currentCmd->isUserCommand()) { emit userCommandOutput(QString::fromUtf8(line) + '\n'); } else { emit internalCommandOutput(QString::fromUtf8(line) + '\n'); } // protect against wild replies that sometimes returned from gdb without a pending command if (!m_currentCmd) { qCWarning(DEBUGGERCOMMON) << "Received a result without a pending command"; throw std::runtime_error("Received a result without a pending command"); } else if (m_currentCmd->token() != result.token) { std::stringstream ss; ss << "Received a result with token not matching pending command. " << "Pending: " << m_currentCmd->token() << "Received: " << result.token; qCWarning(DEBUGGERCOMMON) << ss.str().c_str(); throw std::runtime_error(ss.str()); } // GDB doc: "running" and "exit" are status codes equivalent to "done" if (result.reason == QLatin1String("done") || result.reason == QLatin1String("running") || result.reason == QLatin1String("exit")) { qCDebug(DEBUGGERCOMMON) << "Result token is" << result.token; m_currentCmd->markAsCompleted(); qCDebug(DEBUGGERCOMMON) << "Command successful, times " << m_currentCmd->totalProcessingTime() << m_currentCmd->queueTime() << m_currentCmd->gdbProcessingTime(); m_currentCmd->invokeHandler(result); } else if (result.reason == QLatin1String("error")) { qCDebug(DEBUGGERCOMMON) << "Handling error"; m_currentCmd->markAsCompleted(); qCDebug(DEBUGGERCOMMON) << "Command error, times" << m_currentCmd->totalProcessingTime() << m_currentCmd->queueTime() << m_currentCmd->gdbProcessingTime(); // Some commands want to handle errors themself. if (m_currentCmd->handlesError() && m_currentCmd->invokeHandler(result)) { qCDebug(DEBUGGERCOMMON) << "Invoked custom handler\n"; // Done, nothing more needed } else emit error(result); } else { qCDebug(DEBUGGERCOMMON) << "Unhandled result code: " << result.reason; } delete m_currentCmd; m_currentCmd = nullptr; emit ready(); break; } case MI::Record::Async: { MI::AsyncRecord& async = static_cast(*r); switch (async.subkind) { case MI::AsyncRecord::Exec: { // Prefix '*'; asynchronous state changes of the target if (async.reason == QLatin1String("stopped")) { emit programStopped(async); } else if (async.reason == QLatin1String("running")) { emit programRunning(); } else { qCDebug(DEBUGGERCOMMON) << "Unhandled exec notification: " << async.reason; } break; } case MI::AsyncRecord::Notify: { // Prefix '='; supplementary information that we should handle (new breakpoint etc.) emit notification(async); break; } case MI::AsyncRecord::Status: { // Prefix '+'; GDB documentation: // On-going status information about progress of a slow operation; may be ignored break; } default: Q_ASSERT(false); } break; } case MI::Record::Stream: { MI::StreamRecord& s = static_cast(*r); if (s.subkind == MI::StreamRecord::Target) { emit applicationOutput(s.message); } else if (s.subkind == MI::StreamRecord::Console) { if (m_currentCmd && m_currentCmd->isUserCommand()) emit userCommandOutput(s.message); else emit internalCommandOutput(s.message); if (m_currentCmd) m_currentCmd->newOutput(s.message); } else { emit debuggerInternalOutput(s.message); } emit streamRecord(s); break; } case MI::Record::Prompt: break; } #ifndef DEBUG_NO_TRY } catch(const std::exception& e) { KMessageBox::detailedSorry( qApp->activeWindow(), i18nc("Internal debugger error", "

The debugger component encountered internal error while " "processing reply from gdb. Please submit a bug report. " "The debug session will now end to prevent potential crash"), i18n("The exception is: %1\n" "The MI response is: %2", e.what(), QString::fromLatin1(line)), i18n("Internal debugger error")); emit exited(true, e.what()); } #endif } void MIDebugger::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(DEBUGGERCOMMON) << "Debugger FINISHED\n"; bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit; emit userCommandOutput(QStringLiteral("Process exited\n")); emit exited(abnormal, i18n("Process exited")); } void MIDebugger::processErrored(QProcess::ProcessError error) { qCDebug(DEBUGGERCOMMON) << "Debugger ERRORED" << error; if(error == QProcess::FailedToStart) { KMessageBox::information( qApp->activeWindow(), i18n("Could not start debugger." "

Could not run '%1'. " "Make sure that the path name is specified correctly.", - debuggerExecutable_), + m_debuggerExecutable), i18n("Could not start debugger")); emit userCommandOutput(QStringLiteral("Process failed to start\n")); emit exited(true, i18n("Process failed to start")); } else if (error == QProcess::Crashed) { KMessageBox::error( qApp->activeWindow(), i18n("Debugger crashed." "

The debugger process '%1' crashed.
" "Because of that the debug session has to be ended.
" "Try to reproduce the crash without KDevelop and report a bug.
", - debuggerExecutable_), + m_debuggerExecutable), i18n("Debugger crashed")); emit userCommandOutput(QStringLiteral("Process crashed\n")); emit exited(true, i18n("Process crashed")); } } diff --git a/plugins/debuggercommon/midebugger.h b/plugins/debuggercommon/midebugger.h index 5677444be1..f934c029cf 100644 --- a/plugins/debuggercommon/midebugger.h +++ b/plugins/debuggercommon/midebugger.h @@ -1,151 +1,151 @@ /* * Low level Debugger MI interface. * * Copyright 2007 Vladimir Prus * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MIDEBUGGER_H #define MIDEBUGGER_H #include "mi/mi.h" #include "mi/miparser.h" #include #include #include class KConfigGroup; class KProcess; namespace KDevMI { namespace MI { class MICommand; } class MIDebugger : public QObject { Q_OBJECT public: explicit MIDebugger(QObject* parent = nullptr); ~MIDebugger() override; /** Starts the debugger. This should be done after connecting to all signals the client is interested in. */ virtual bool start(KConfigGroup& config, const QStringList& extraArguments = {}) = 0; /** Executes a command. This method may be called at most once each time 'ready' is emitted. When the debugger instance is just constructed, one should wait for 'ready' as well. The ownership of 'command' is transferred to the debugger. */ void execute(MI::MICommand* command); /** Returns true if 'execute' can be called immediately. */ bool isReady() const; /** FIXME: temporary, to be eliminated. */ MI::MICommand* currentCommand() const; /** Arrange to debugger to stop doing whatever it's doing, and start waiting for a command. FIXME: probably should make sure that 'ready' is emitted, or something. */ void interrupt(); /** Kills the debugger. */ void kill(); Q_SIGNALS: /** Emitted when debugger becomes ready -- i.e. when isReady call will return true. */ void ready(); /** Emitted when the debugger itself exits. This could happen because it just crashed due to internal bug, or we killed it explicitly. */ void exited(bool abnormal, const QString &msg); /** Emitted when debugger reports stop, with 'r' being the data provided by the debugger. */ void programStopped(const MI::AsyncRecord& r); /** Emitted when debugger believes that the program is running. */ void programRunning(); /** Emitted for each MI stream record found. Presently only used to recognize some CLI messages that mean that the program has died. FIXME: connect to parseCliLine */ void streamRecord(const MI::StreamRecord& s); /** Reports an async notification record. */ void notification(const MI::AsyncRecord& n); /** Emitted for error that is not handled by the command being executed. */ void error(const MI::ResultRecord& s); /** Reports output from the running application. Generally output will only be available when using remote debugger targets. When running locally, the output will either appear on debugger stdout, and ignored, or routed via pty. */ void applicationOutput(const QString& s); /** Reports output of a command explicitly typed by the user, or output from .gdbinit commands. */ void userCommandOutput(const QString& s); /** Reports output of a command issued internally by KDevelop. */ void internalCommandOutput(const QString& s); /** Reports debugger interal output, including stderr output from debugger and the 'log' MI channel */ void debuggerInternalOutput(const QString& s); protected Q_SLOTS: void readyReadStandardOutput(); void readyReadStandardError(); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void processErrored(QProcess::ProcessError); protected: void processLine(const QByteArray& line); protected: - QString debuggerExecutable_; + QString m_debuggerExecutable; KProcess* m_process; MI::MICommand* m_currentCmd; MI::MIParser m_parser; /** The unprocessed output from debugger. Output is processed as soon as we see newline. */ QByteArray m_buffer; }; } #endif diff --git a/plugins/gdb/gdb.cpp b/plugins/gdb/gdb.cpp index 463cbe237d..993e657167 100644 --- a/plugins/gdb/gdb.cpp +++ b/plugins/gdb/gdb.cpp @@ -1,98 +1,98 @@ /* * Low level GDB interface. * * Copyright 1999 John Birch * Copyright 2007 Vladimir Prus * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gdb.h" #include "dbgglobal.h" #include "debuglog.h" #include #include #include #include #include #include #include using namespace KDevMI::GDB; using namespace KDevMI::MI; GdbDebugger::GdbDebugger(QObject* parent) : MIDebugger(parent) { } GdbDebugger::~GdbDebugger() { } bool GdbDebugger::start(KConfigGroup& config, const QStringList& extraArguments) { // FIXME: verify that default value leads to something sensible QUrl gdbUrl = config.readEntry(Config::GdbPathEntry, QUrl()); if (gdbUrl.isEmpty()) { - debuggerExecutable_ = QStringLiteral("gdb"); + m_debuggerExecutable = QStringLiteral("gdb"); } else { // FIXME: verify its' a local path. - debuggerExecutable_ = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); + m_debuggerExecutable = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); } QStringList arguments = extraArguments; arguments << QStringLiteral("--interpreter=mi2") << QStringLiteral("-quiet"); QUrl shell = config.readEntry(Config::DebuggerShellEntry, QUrl()); if(!shell.isEmpty()) { qCDebug(DEBUGGERGDB) << "have shell" << shell; QString shell_without_args = shell.toLocalFile().split(QChar(' ')).first(); QFileInfo info(shell_without_args); /*if( info.isRelative() ) { shell_without_args = build_dir + "/" + shell_without_args; info.setFile( shell_without_args ); }*/ if(!info.exists()) { KMessageBox::information( qApp->activeWindow(), i18n("Could not locate the debugging shell '%1'.", shell_without_args ), i18n("Debugging Shell Not Found") ); return false; } - arguments.insert(0, debuggerExecutable_); + arguments.insert(0, m_debuggerExecutable); arguments.insert(0, shell.toLocalFile()); m_process->setShellCommand(KShell::joinArgs(arguments)); } else { - m_process->setProgram(debuggerExecutable_, arguments); + m_process->setProgram(m_debuggerExecutable, arguments); } m_process->start(); - qCDebug(DEBUGGERGDB) << "Starting GDB with command" << shell.toLocalFile() + QLatin1Char(' ') + debuggerExecutable_ + qCDebug(DEBUGGERGDB) << "Starting GDB with command" << shell.toLocalFile() + QLatin1Char(' ') + m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')); qCDebug(DEBUGGERGDB) << "GDB process pid:" << m_process->pid(); - emit userCommandOutput(shell.toLocalFile() + QLatin1Char(' ') + debuggerExecutable_ + emit userCommandOutput(shell.toLocalFile() + QLatin1Char(' ') + m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')) + QLatin1Char('\n')); return true; } diff --git a/plugins/lldb/lldbdebugger.cpp b/plugins/lldb/lldbdebugger.cpp index 14bd17761d..f621e3b869 100644 --- a/plugins/lldb/lldbdebugger.cpp +++ b/plugins/lldb/lldbdebugger.cpp @@ -1,85 +1,85 @@ /* * Low level LLDB interface. * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "lldbdebugger.h" #include "dbgglobal.h" #include "debuglog.h" #include #include #include #include #include using namespace KDevelop; using namespace KDevMI::LLDB; using namespace KDevMI::MI; LldbDebugger::LldbDebugger(QObject* parent) : MIDebugger(parent) { } LldbDebugger::~LldbDebugger() { } bool LldbDebugger::start(KConfigGroup& config, const QStringList& extraArguments) { // Get path to executable QUrl lldbUrl = config.readEntry(Config::LldbExecutableEntry, QUrl()); if (!lldbUrl.isValid() || !lldbUrl.isLocalFile()) { - debuggerExecutable_ = QStringLiteral("lldb-mi"); + m_debuggerExecutable = QStringLiteral("lldb-mi"); } else { - debuggerExecutable_ = lldbUrl.toLocalFile(); + m_debuggerExecutable = lldbUrl.toLocalFile(); } // Get arguments QStringList arguments = extraArguments; //arguments << "-quiet"; arguments.append(KShell::splitArgs(config.readEntry(Config::LldbArgumentsEntry, QString()))); // Get environment const EnvironmentProfileList egl(config.config()); const auto &envs = egl.variables(config.readEntry(Config::LldbEnvironmentEntry, egl.defaultProfileName())); QProcessEnvironment processEnv; if (config.readEntry(Config::LldbInheritSystemEnvEntry, true)) { processEnv = QProcessEnvironment::systemEnvironment(); } for (auto it = envs.begin(), ite = envs.end(); it != ite; ++it) { processEnv.insert(it.key(), it.value()); } // Start! m_process->setProcessEnvironment(processEnv); - m_process->setProgram(debuggerExecutable_, arguments); + m_process->setProgram(m_debuggerExecutable, arguments); m_process->start(); - qCDebug(DEBUGGERLLDB) << "Starting LLDB with command" << debuggerExecutable_ + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')); + qCDebug(DEBUGGERLLDB) << "Starting LLDB with command" << m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')); qCDebug(DEBUGGERLLDB) << "LLDB process pid:" << m_process->pid(); - emit userCommandOutput(debuggerExecutable_ + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')) + QLatin1Char('\n')); + emit userCommandOutput(m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')) + QLatin1Char('\n')); return true; }