diff --git a/debuggers/common/debuggerbase.cpp b/debuggers/common/debuggerbase.cpp index 088bd6cf57..43026b8d40 100644 --- a/debuggers/common/debuggerbase.cpp +++ b/debuggers/common/debuggerbase.cpp @@ -1,334 +1,334 @@ /* * 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 "debuggerbase.h" #include "debuglog.h" #include "mi/micommand.h" #include "sys/signal.h" #include #include #include #include #include #include // #define DEBUG_NO_TRY //to get a backtrace to where the exception was thrown using namespace KDevDebugger; using namespace KDevDebugger::MI; DebuggerBase::DebuggerBase(QObject* parent) : QObject(parent) , process_(nullptr) , currentCmd_(nullptr) { process_ = new KProcess(this); process_->setOutputChannelMode(KProcess::SeparateChannels); connect(process_, &KProcess::readyReadStandardOutput, this, &DebuggerBase::readyReadStandardOutput); connect(process_, &KProcess::readyReadStandardError, this, &DebuggerBase::readyReadStandardError); connect(process_, static_cast(&KProcess::finished), this, &DebuggerBase::processFinished); connect(process_, static_cast(&KProcess::error), this, &DebuggerBase::processErrored); } DebuggerBase::~DebuggerBase() { // prevent Qt warning: QProcess: Destroyed while process is still running. if (process_ && process_->state() == QProcess::Running) { disconnect(process_, static_cast(&KProcess::error), this, &DebuggerBase::processErrored); process_->kill(); process_->waitForFinished(10); } } void DebuggerBase::execute(MICommand* command) { currentCmd_ = command; QString commandText = currentCmd_->cmdToSend(); qCDebug(DEBUGGERCOMMON) << "SEND:" << commandText.trimmed(); QByteArray commandUtf8 = commandText.toUtf8(); process_->write(commandUtf8, commandUtf8.length()); QString prettyCmd = currentCmd_->cmdToSend(); prettyCmd.remove( QRegExp("set prompt \032.\n") ); prettyCmd = "(gdb) " + prettyCmd; if (currentCmd_->isUserCommand()) emit userCommandOutput(prettyCmd); else emit internalCommandOutput(prettyCmd); } bool DebuggerBase::isReady() const { return currentCmd_ == 0; } void DebuggerBase::interrupt() { //TODO:win32 Porting needed int pid = process_->pid(); if (pid != 0) { ::kill(pid, SIGINT); } } MICommand* DebuggerBase::currentCommand() const { return currentCmd_; } void DebuggerBase::kill() { process_->kill(); } void DebuggerBase::readyReadStandardOutput() { process_->setReadChannel(QProcess::StandardOutput); buffer_ += process_->readAll(); for (;;) { /* In MI mode, all messages are exactly one line. See if we have any complete lines in the buffer. */ int i = buffer_.indexOf('\n'); if (i == -1) break; QByteArray reply(buffer_.left(i)); buffer_ = buffer_.mid(i+1); processLine(reply); } } void DebuggerBase::readyReadStandardError() { process_->setReadChannel(QProcess::StandardOutput); emit internalCommandOutput(QString::fromUtf8(process_->readAll())); } void DebuggerBase::processLine(const QByteArray& line) { - qCDebug(DEBUGGERCOMMON) << "Debugger output: " << line; + qCDebug(DEBUGGERCOMMON) << "Debugger (" << process_->pid() <<") output: " << line; FileSymbol file; file.contents = line; std::unique_ptr r(mi_parser_.parse(&file)); if (!r) { // FIXME: Issue an error! 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); emit internalCommandOutput(QString::fromUtf8(line) + '\n'); // GDB doc: "running" and "exit" are status codes equivalent to "done" if (result.reason == "done" || result.reason == "running" || result.reason == "exit") { if (!currentCmd_) { qCDebug(DEBUGGERCOMMON) << "Received a result without a pending command"; } else { qCDebug(DEBUGGERCOMMON) << "Result token is" << result.token; Q_ASSERT(currentCmd_->token() == result.token); currentCmd_->invokeHandler(result); } } else if (result.reason == "error") { qCDebug(DEBUGGERCOMMON) << "Handling error"; // Some commands want to handle errors themself. if (currentCmd_->handlesError() && 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 currentCmd_; currentCmd_ = nullptr; emit ready(); break; } case MI::Record::Async: { MI::AsyncRecord& async = dynamic_cast(*r); switch (async.subkind) { case MI::AsyncRecord::Exec: { // Prefix '*'; asynchronous state changes of the target if (async.reason == "stopped") { emit programStopped(async); } else if (async.reason == "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 = dynamic_cast(*r); if (s.subkind == MI::StreamRecord::Target) { emit applicationOutput(s.message); } else { if (currentCmd_ && currentCmd_->isUserCommand()) emit userCommandOutput(s.message); else if (s.subkind == MI::StreamRecord::Console) { emit applicationOutput(s.message); } else { emit internalCommandOutput(s.message); } if (currentCmd_) currentCmd_->newOutput(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."), i18n("The exception is: %1\n" "The MI response is: %2", e.what(), QString::fromLatin1(line)), i18n("Internal debugger error")); } #endif } // ************************************************************************** void DebuggerBase::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(DEBUGGERCOMMON) << "GDB FINISHED\n"; bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit; emit userCommandOutput("(gdb) Process exited\n"); emit exited(abnormal, i18n("Process exited")); } void DebuggerBase::processErrored(QProcess::ProcessError error) { qCDebug(DEBUGGERCOMMON) << "GDB 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.", debuggerBinary_), i18n("Could not start debugger")); emit userCommandOutput("(gdb) didn't start\n"); emit exited(true, i18n("Process didn't start'")); } else if (error == QProcess::Crashed) { KMessageBox::error( qApp->activeWindow(), i18n("Gdb crashed." "

Because of that the debug session has to be ended.
" "Try to reproduce the crash with plain gdb and report a bug.
"), i18n("Gdb crashed")); emit userCommandOutput("(gdb) Process crashed\n"); emit exited(true, i18n("Process crashed")); } } diff --git a/debuggers/gdb/gdb.cpp b/debuggers/gdb/gdb.cpp index 692c3af768..f2de1aaa82 100644 --- a/debuggers/gdb/gdb.cpp +++ b/debuggers/gdb/gdb.cpp @@ -1,98 +1,99 @@ /* * 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 #include using namespace KDevDebugger::GDB; using namespace KDevDebugger::MI; GDB::GDB(QObject* parent) : DebuggerBase(parent) { } GDB::~GDB() { } void GDB::start(KConfigGroup& config, const QStringList& extraArguments) { // FIXME: verify that default value leads to something sensible QUrl gdbUrl = config.readEntry(gdbPathEntry, QUrl()); if (gdbUrl.isEmpty()) { debuggerBinary_ = "gdb"; } else { // FIXME: verify its' a local path. debuggerBinary_ = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); } QStringList arguments = extraArguments; arguments << "--interpreter=mi2" << "-quiet"; QUrl shell = config.readEntry(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") ); // FIXME: throw, or set some error message. return; } arguments.insert(0, debuggerBinary_); arguments.insert(0, shell.toLocalFile()); process_->setShellCommand(KShell::joinArgs(arguments)); } else { process_->setProgram(debuggerBinary_, arguments); } process_->start(); qCDebug(DEBUGGERGDB) << "Starting GDB with command" << shell.toLocalFile() + ' ' + debuggerBinary_ + ' ' + arguments.join(' '); + qCDebug(DEBUGGERGDB) << "GDB process pid:" << process_->pid(); emit userCommandOutput(shell.toLocalFile() + ' ' + debuggerBinary_ + ' ' + arguments.join(' ') + '\n'); }