diff --git a/debuggers/CMakeLists.txt b/debuggers/CMakeLists.txt --- a/debuggers/CMakeLists.txt +++ b/debuggers/CMakeLists.txt @@ -1,4 +1,7 @@ +include_directories(common) + if(NOT WIN32) - add_subdirectory(gdb) + add_subdirectory(common) + add_subdirectory(gdb) endif() diff --git a/debuggers/common/CMakeLists.txt b/debuggers/common/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/debuggers/common/CMakeLists.txt @@ -0,0 +1,27 @@ +set(debuggercommon_SRCS + mi/mi.cpp + mi/milexer.cpp + mi/miparser.cpp + mi/micommand.cpp + mi/micommandqueue.cpp + debuglog.cpp + midebugger.cpp + midebugsession.cpp + mibreakpointcontroller.cpp + miframestackmodel.cpp + mivariablecontroller.cpp + mivariable.cpp + stringhelpers.cpp + stty.cpp +) +#ki18n_wrap_ui(debuggercommon_SRCS something.ui) + +add_library(kdevdebuggercommon STATIC ${debuggercommon_SRCS}) +target_link_libraries(kdevdebuggercommon + PUBLIC + KDev::Debugger + PRIVATE + Qt5::Core +) + +kde_target_enable_exceptions(kdevdebuggercommon PUBLIC) diff --git a/debuggers/gdb/gdbglobal.h b/debuggers/common/dbgglobal.h rename from debuggers/gdb/gdbglobal.h rename to debuggers/common/dbgglobal.h --- a/debuggers/gdb/gdbglobal.h +++ b/debuggers/common/dbgglobal.h @@ -1,5 +1,5 @@ /*************************************************************************** - gdbglobal.h + dbgglobal.h ------------------- begin : Sun Aug 8 1999 copyright : (C) 1999 by John Birch @@ -15,12 +15,12 @@ * * ***************************************************************************/ -#ifndef _GDBGLOBAL_H_ -#define _GDBGLOBAL_H_ +#ifndef _DBGGLOBAL_H_ +#define _DBGGLOBAL_H_ #include -namespace GDBDebugger +namespace KDevMI { enum DBGStateFlag @@ -50,8 +50,20 @@ typeStruct, typeArray, typeQString, typeWhitespace, typeName }; -} +// FIXME: find a more appropriate place for these strings. Possibly a place specific to debugger backend +static const char gdbPathEntry[] = "GDB Path"; +static const char debuggerShellEntry[] = "Debugger Shell"; +static const char remoteGdbConfigEntry[] = "Remote GDB Config Script"; +static const char remoteGdbShellEntry[] = "Remote GDB Shell Script"; +static const char remoteGdbRunEntry[] = "Remote GDB Run Script"; +static const char staticMembersEntry[] = "Display Static Members"; +static const char demangleNamesEntry[] = "Display Demangle Names"; +static const char allowForcedBPEntry[] = "Allow Forced Breakpoint Set"; +static const char startWithEntry[] = "Start With"; +static const char breakOnStartEntry[] = "Break on Start"; -Q_DECLARE_OPERATORS_FOR_FLAGS(GDBDebugger::DBGStateFlags) +} // end of namespace KDevMI -#endif +Q_DECLARE_OPERATORS_FOR_FLAGS(KDevMI::DBGStateFlags) + +#endif // _DBGGLOBAL_H_ diff --git a/debuggers/common/debuglog.h b/debuggers/common/debuglog.h new file mode 100644 --- /dev/null +++ b/debuggers/common/debuglog.h @@ -0,0 +1,33 @@ +/* + * KDevelop debugger logging facilities + * + * 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 . + * + */ + +#ifndef KDEVELOP_DEBUG_LOG_H +#define KDEVELOP_DEBUG_LOG_H + +#include + +Q_DECLARE_LOGGING_CATEGORY(DEBUGGERCOMMON) +Q_DECLARE_LOGGING_CATEGORY(DEBUGGERGDB) +Q_DECLARE_LOGGING_CATEGORY(DEBUGGERLLDB) + +#endif diff --git a/debuggers/common/debuglog.cpp b/debuggers/common/debuglog.cpp new file mode 100644 --- /dev/null +++ b/debuggers/common/debuglog.cpp @@ -0,0 +1,28 @@ +/* + * KDevelop debugger logging facilities + * + * 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 "debuglog.h" + +Q_LOGGING_CATEGORY(DEBUGGERCOMMON, "kdevelop.debuggers.common") +Q_LOGGING_CATEGORY(DEBUGGERGDB, "kdevelop.debuggers.gdb") +Q_LOGGING_CATEGORY(DEBUGGERLLDB, "kdevelop.debuggers.lldb") diff --git a/debuggers/gdb/mi/gdbmi.h b/debuggers/common/mi/mi.h rename from debuggers/gdb/mi/gdbmi.h rename to debuggers/common/mi/mi.h --- a/debuggers/gdb/mi/gdbmi.h +++ b/debuggers/common/mi/mi.h @@ -3,6 +3,8 @@ * roberto@kdevelop.org * * Copyright (C) 2005-2006 by Vladimir Prus * * ghost@cs.msu.su * + * Copyright (C) 2016 by Aetf * + * aetf@unlimitedcodeworks.xyz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * @@ -23,16 +25,15 @@ #define GDBMI_H #include -#include +#include #include /** @author Roberto Raggi @author Vladimir Prus */ -namespace GDBMI -{ +namespace KDevMI { namespace MI { enum CommandType { NonMI, @@ -254,7 +255,7 @@ converted to int, throws type_error. */ virtual int toInt(int base = 10) const; - + /** If this value is a tuple, returns true if the tuple has a field named 'variable'. Otherwise, throws type_error. @@ -306,7 +307,7 @@ QString literal() const override; int toInt(int base) const override; - + private: QString literal_; }; @@ -322,7 +323,7 @@ const Value& operator[](const QString& variable) const override; QList results; - QMap results_by_name; + QMap results_by_name; }; struct ListValue : public Value @@ -360,7 +361,7 @@ { Record::kind = Result; } - + uint32_t token; QString reason; }; @@ -415,6 +416,7 @@ Subkind subkind; QString message; }; -} +} // end of namespace MI +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/mi/gdbmi.cpp b/debuggers/common/mi/mi.cpp rename from debuggers/gdb/mi/gdbmi.cpp rename to debuggers/common/mi/mi.cpp --- a/debuggers/gdb/mi/gdbmi.cpp +++ b/debuggers/common/mi/mi.cpp @@ -19,9 +19,9 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "gdbmi.h" +#include "mi.h" -using namespace GDBMI; +using namespace KDevMI::MI; type_error::type_error() diff --git a/debuggers/gdb/gdbcommand.h b/debuggers/common/mi/micommand.h rename from debuggers/gdb/gdbcommand.h rename to debuggers/common/mi/micommand.h --- a/debuggers/gdb/gdbcommand.h +++ b/debuggers/common/mi/micommand.h @@ -2,6 +2,8 @@ begin : Sun Aug 8 1999 copyright : (C) 1999 by John Birch email : jbb@kdevelop.org + copyright : (C) 2016 by Aetf + email : aetf@unlimitedcodeworks.xyz ***************************************************************************/ /*************************************************************************** @@ -13,20 +15,18 @@ * * ***************************************************************************/ -#ifndef _GDBCOMMAND_H_ -#define _GDBCOMMAND_H_ +#ifndef _MICOMMAND_H_ +#define _MICOMMAND_H_ -#include +#include "mi/mi.h" #include #include #include -#include "mi/gdbmi.h" - -namespace GDBDebugger -{ +#include +namespace KDevMI { namespace MI { class VarItem; class ValueCallback; @@ -61,26 +61,26 @@ Q_DECLARE_FLAGS(CommandFlags, CommandFlag) //base class for handlers -class GDBCommandHandler +class MICommandHandler { public: - virtual ~GDBCommandHandler() {} - virtual void handle(const GDBMI::ResultRecord&) = 0; + virtual ~MICommandHandler() {} + virtual void handle(const ResultRecord&) = 0; virtual bool handlesError() { return false; } /** * If the handler object should be deleted after the handle() call. */ virtual bool autoDelete() { return true; } }; -class FunctionCommandHandler : public GDBCommandHandler { +class FunctionCommandHandler : public MICommandHandler { public: - typedef std::function Function; + typedef std::function Function; FunctionCommandHandler(const Function& callback, CommandFlags flags = 0); - virtual void handle(const GDBMI::ResultRecord&) override; + virtual void handle(const ResultRecord&) override; virtual bool handlesError() override; private: @@ -92,29 +92,28 @@ * @author John Birch */ -class GDBCommand +class MICommand { public: - GDBCommand(GDBMI::CommandType type, const QString& arguments = QString(), CommandFlags flags = 0); + MICommand(CommandType type, const QString& arguments = QString(), CommandFlags flags = 0); template - GDBCommand(GDBMI::CommandType type, const QString& arguments, - Handler* handler_this, - void (Handler::* handler_method)(const GDBMI::ResultRecord&), - CommandFlags flags = 0); + MICommand(CommandType type, const QString& arguments, + Handler* handler_this, + void (Handler::* handler_method)(const ResultRecord&), + CommandFlags flags = 0); - GDBCommand(GDBMI::CommandType type, const QString& arguments, GDBCommandHandler* handler, - CommandFlags flags = 0); + MICommand(CommandType type, const QString& arguments, MICommandHandler* handler, + CommandFlags flags = 0); - GDBCommand( - GDBMI::CommandType type, const QString& arguments, - const FunctionCommandHandler::Function& callback, - CommandFlags flags = 0); + MICommand(CommandType type, const QString& arguments, + const FunctionCommandHandler::Function& callback, + CommandFlags flags = 0); - virtual ~GDBCommand(); + virtual ~MICommand(); - GDBMI::CommandType type() const; - QString gdbCommand() const; + CommandType type() const; + QString miCommand() const; CommandFlags flags() const {return flags_;} @@ -125,7 +124,7 @@ uint32_t token() const {return token_;} /** - * Set the MI token. This is done by \ref GDBCommandQueue. + * Set the MI token. This is done by \ref MICommandQueue. */ void setToken(uint32_t token) {token_ = token;} @@ -156,9 +155,9 @@ * * The command object assumes ownership of @p handler. */ - void setHandler(GDBCommandHandler* handler); - - /* The command that should be sent to gdb. + void setHandler(MICommandHandler* handler); + + /* The command that should be sent to debugger. This method is virtual so the command can compute this dynamically, possibly using results of the previous commands. @@ -179,14 +178,14 @@ // If there's a handler for this command, invokes it and returns true. // Otherwise, returns false. - bool invokeHandler(const GDBMI::ResultRecord& r); + bool invokeHandler(const ResultRecord& r); // Returns 'true' if 'invokeHandler' should be invoked even // on MI errors. bool handlesError() const; - // Called by gdbcontroller for each new output string - // gdb emits for this command. In MI mode, this includes + // Called by debuggercontroller for each new output string + // debugger emits for this command. In MI mode, this includes // all "stream" messages, but does not include MI responses. void newOutput(const QString&); @@ -199,36 +198,36 @@ bool stateReloading() const; private: - GDBMI::CommandType type_; + CommandType type_; CommandFlags flags_; uint32_t token_ = 0; QString command_; - GDBCommandHandler *commandHandler_; + MICommandHandler *commandHandler_; QStringList lines; bool stateReloading_; private: int m_thread; int m_frame; }; -class UserCommand : public GDBCommand +class UserCommand : public MICommand { public: - UserCommand(GDBMI::CommandType type, const QString& s); + UserCommand(CommandType type, const QString& s); bool isUserCommand() const override; }; /** This is a class for raw CLI commands. Instead of invoking user provided hook with MI response, it invokes the a hook with lists of strings. */ -class CliCommand : public GDBCommand +class CliCommand : public MICommand { public: template - CliCommand(GDBMI::CommandType type, const QString& command, + CliCommand(CommandType type, const QString& command, Handler* handler_this, void (Handler::* handler_method)(const QStringList&), CommandFlags flags = 0); @@ -238,16 +237,16 @@ a user provided handler when all preceeding commands are executed. */ -class SentinelCommand : public GDBCommand +class SentinelCommand : public MICommand { public: typedef std::function Function; template SentinelCommand(Handler* handler_this, void (Handler::* handler_method)(), CommandFlags flags = 0) - : GDBCommand(GDBMI::NonMI, QString(), flags) + : MICommand(NonMI, QString(), flags) { QPointer guarded_this(handler_this); handler = [guarded_this, handler_method]() { @@ -258,12 +257,12 @@ } SentinelCommand(const Function& handler, CommandFlags flags = 0) - : GDBCommand(GDBMI::NonMI, QString(), flags) + : MICommand(NonMI, QString(), flags) , handler(handler) { } - using GDBCommand::invokeHandler; + using MICommand::invokeHandler; void invokeHandler() { handler(); @@ -278,7 +277,7 @@ Function handler; }; -class ExpressionValueCommand : public QObject, public GDBCommand +class ExpressionValueCommand : public QObject, public MICommand { public: typedef void (QObject::*handler_method_t)(const QString&); @@ -288,13 +287,13 @@ const QString& expression, Handler* handler_this, void (Handler::* handler_method)(const QString&)) - : GDBCommand(GDBMI::DataEvaluateExpression, expression, this, + : MICommand(DataEvaluateExpression, expression, this, &ExpressionValueCommand::handleResponse), handler_this(handler_this), handler_method(static_cast(handler_method)) {} - void handleResponse(const GDBMI::ResultRecord& r) + void handleResponse(const ResultRecord& r) { (handler_this.data()->*handler_method)(r["value"].literal()); } @@ -306,11 +305,11 @@ template -GDBCommand::GDBCommand( - GDBMI::CommandType type, +MICommand::MICommand( + CommandType type, const QString& command, Handler* handler_this, - void (Handler::* handler_method)(const GDBMI::ResultRecord&), + void (Handler::* handler_method)(const ResultRecord&), CommandFlags flags) : type_(type), flags_(flags & ~CmdHandlesError), @@ -321,32 +320,33 @@ m_frame(-1) { QPointer guarded_this(handler_this); - setHandler(new FunctionCommandHandler([guarded_this, handler_method](const GDBMI::ResultRecord& r) { + setHandler(new FunctionCommandHandler([guarded_this, handler_method](const ResultRecord& r) { if (guarded_this) { (guarded_this.data()->*handler_method)(r); } }, flags)); } template CliCommand::CliCommand( - GDBMI::CommandType type, + CommandType type, const QString& command, Handler* handler_this, void (Handler::* handler_method)(const QStringList&), CommandFlags flags) -: GDBCommand(type, command) +: MICommand(type, command) { QPointer guarded_this(handler_this); - setHandler(new FunctionCommandHandler([this, guarded_this, handler_method](const GDBMI::ResultRecord&) { + setHandler(new FunctionCommandHandler([this, guarded_this, handler_method](const ResultRecord&) { if (guarded_this) { (guarded_this.data()->*handler_method)(this->allStreamOutput()); } }, flags)); } -} +} // end of namespace MI +} // end of namespace KDevMI -Q_DECLARE_OPERATORS_FOR_FLAGS(GDBDebugger::CommandFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(KDevMI::MI::CommandFlags) #endif diff --git a/debuggers/gdb/gdbcommand.cpp b/debuggers/common/mi/micommand.cpp rename from debuggers/gdb/gdbcommand.cpp rename to debuggers/common/mi/micommand.cpp --- a/debuggers/gdb/gdbcommand.cpp +++ b/debuggers/common/mi/micommand.cpp @@ -13,12 +13,9 @@ * * ***************************************************************************/ -#include "gdbcommand.h" +#include "micommand.h" -using namespace GDBMI; - -namespace GDBDebugger -{ +using namespace KDevMI::MI; FunctionCommandHandler::FunctionCommandHandler(const FunctionCommandHandler::Function& callback, CommandFlags flags) : _flags(flags) @@ -37,20 +34,30 @@ } -GDBCommand::GDBCommand(GDBMI::CommandType type, const QString& command, CommandFlags flags) -: type_(type), flags_(flags & ~CmdHandlesError), command_(command), commandHandler_(0), - stateReloading_(false), m_thread(-1), m_frame(-1) +MICommand::MICommand(CommandType type, const QString& command, CommandFlags flags) + : type_(type) + , flags_(flags & ~CmdHandlesError) + , command_(command) + , commandHandler_(0) + , stateReloading_(false) + , m_thread(-1) + , m_frame(-1) { } -GDBCommand::GDBCommand(CommandType type, const QString& arguments, GDBCommandHandler* handler, +MICommand::MICommand(CommandType type, const QString& arguments, MICommandHandler* handler, CommandFlags flags) -: type_(type), flags_(flags), command_(arguments), commandHandler_(handler), - stateReloading_(false), m_thread(-1), m_frame(-1) + : type_(type) + , flags_(flags) + , command_(arguments) + , commandHandler_(handler) + , stateReloading_(false) + , m_thread(-1) + , m_frame(-1) { } -GDBCommand::GDBCommand(CommandType type, const QString& arguments, +MICommand::MICommand(CommandType type, const QString& arguments, const FunctionCommandHandler::Function& callback, CommandFlags flags) : type_(type) , flags_(flags & ~CmdHandlesError) @@ -62,28 +69,28 @@ { } -GDBCommand::~GDBCommand() +MICommand::~MICommand() { if (commandHandler_ && commandHandler_->autoDelete()) { delete commandHandler_; } commandHandler_ = nullptr; } -QString GDBCommand::cmdToSend() +QString MICommand::cmdToSend() { return initialString() + '\n'; } -QString GDBCommand::initialString() const +QString MICommand::initialString() const { QString result = QString::number(token()); if (type() == NonMI) { result += command_; } else { - result += gdbCommand(); + result += miCommand(); if (m_thread != -1) result = result + QString(" --thread %1").arg(m_thread); @@ -97,23 +104,24 @@ return result; } -bool GDBCommand::isUserCommand() const +bool MICommand::isUserCommand() const { return false; } -void GDBCommand::setHandler(GDBCommandHandler* handler) +void MICommand::setHandler(MICommandHandler* handler) { if (commandHandler_ && commandHandler_->autoDelete()) delete commandHandler_; commandHandler_ = handler; } -bool -GDBCommand::invokeHandler(const GDBMI::ResultRecord& r) +bool MICommand::invokeHandler(const ResultRecord& r) { if (commandHandler_) { - bool autoDelete = commandHandler_->autoDelete(); //ask before calling handler as it might deleted itself in handler + //ask before calling handler as it might deleted itself in handler + bool autoDelete = commandHandler_->autoDelete(); + commandHandler_->handle(r); if (autoDelete) { delete commandHandler_; @@ -125,32 +133,32 @@ } } -void GDBCommand::newOutput(const QString& line) +void MICommand::newOutput(const QString& line) { lines.push_back(line); } -const QStringList& GDBCommand::allStreamOutput() const +const QStringList& MICommand::allStreamOutput() const { return lines; } -bool GDBCommand::handlesError() const +bool MICommand::handlesError() const { return commandHandler_ ? commandHandler_->handlesError() : false; } -UserCommand::UserCommand(GDBMI::CommandType type, const QString& s) -: GDBCommand(type, s, CmdMaybeStartsRunning) +UserCommand::UserCommand(CommandType type, const QString& s) + : MICommand(type, s, CmdMaybeStartsRunning) { } bool UserCommand::isUserCommand() const { return true; } -QString GDBCommand::gdbCommand() const +QString MICommand::miCommand() const { QString command; @@ -572,44 +580,42 @@ return '-' + command; } -GDBMI::CommandType GDBCommand::type() const +CommandType MICommand::type() const { return type_; } -int GDBCommand::thread() const +int MICommand::thread() const { return m_thread; } -void GDBCommand::setThread(int thread) +void MICommand::setThread(int thread) { m_thread = thread; } -int GDBCommand::frame() const +int MICommand::frame() const { return m_frame; } -void GDBCommand::setFrame(int frame) +void MICommand::setFrame(int frame) { m_frame = frame; } -QString GDBCommand::command() const +QString MICommand::command() const { return command_; } -void GDBCommand::setStateReloading(bool f) +void MICommand::setStateReloading(bool f) { stateReloading_ = f; } -bool GDBCommand::stateReloading() const +bool MICommand::stateReloading() const { return stateReloading_; } - -} diff --git a/debuggers/gdb/gdbcommandqueue.h b/debuggers/common/mi/micommandqueue.h rename from debuggers/gdb/gdbcommandqueue.h rename to debuggers/common/mi/micommandqueue.h --- a/debuggers/gdb/gdbcommandqueue.h +++ b/debuggers/common/mi/micommandqueue.h @@ -1,5 +1,5 @@ // ************************************************************************* -// gdbcommandqueue.cpp +// micommandqueue.cpp // ------------------- // begin : Wed Dec 5, 2007 // copyright : (C) 2007 by Hamish Rodda @@ -15,25 +15,24 @@ // * * // ************************************************************************** -#ifndef GDBCOMMANDQUEUE_H -#define GDBCOMMANDQUEUE_H +#ifndef MICOMMANDQUEUE_H +#define MICOMMANDQUEUE_H -#include +#include "dbgglobal.h" -#include "gdbglobal.h" +#include -namespace GDBDebugger -{ +namespace KDevMI { namespace MI { -class GDBCommand; +class MICommand; class CommandQueue { public: CommandQueue(); ~CommandQueue(); - void enqueue(GDBCommand* command); + void enqueue(MICommand* command); bool isEmpty() const; int count() const; @@ -45,17 +44,18 @@ /** * Retrieve and remove the next command from the list. */ - GDBCommand* nextCommand(); + MICommand* nextCommand(); private: - void rationalizeQueue(GDBCommand* command); + void rationalizeQueue(MICommand* command); void removeVariableUpdates(); - - QList m_commandList; + + QList m_commandList; int m_immediatelyCounter = 0; uint32_t m_tokenCounter; }; -} +} // end of namespace MI +} // end of namespace KDevMI -#endif // GDBCOMMANDQUEUE_H +#endif // MICOMMANDQUEUE_H diff --git a/debuggers/gdb/gdbcommandqueue.cpp b/debuggers/common/mi/micommandqueue.cpp rename from debuggers/gdb/gdbcommandqueue.cpp rename to debuggers/common/mi/micommandqueue.cpp --- a/debuggers/gdb/gdbcommandqueue.cpp +++ b/debuggers/common/mi/micommandqueue.cpp @@ -15,13 +15,12 @@ // * * // ************************************************************************** -#include "gdbcommandqueue.h" +#include "micommandqueue.h" -#include "mi/gdbmi.h" -#include "gdbcommand.h" +#include "mi.h" +#include "micommand.h" -using namespace GDBDebugger; -using namespace GDBMI; +using namespace KDevMI::MI; CommandQueue::CommandQueue() : m_tokenCounter(0) @@ -33,7 +32,7 @@ qDeleteAll(m_commandList); } -void CommandQueue::enqueue(GDBCommand* command) +void CommandQueue::enqueue(MICommand* command) { ++m_tokenCounter; if (m_tokenCounter == 0) @@ -48,19 +47,19 @@ rationalizeQueue(command); } -void CommandQueue::rationalizeQueue(GDBCommand * command) +void CommandQueue::rationalizeQueue(MICommand * command) { if (command->type() >= ExecAbort && command->type() <= ExecUntil) // Changing execution location, abort any variable updates removeVariableUpdates(); } void CommandQueue::removeVariableUpdates() { - QMutableListIterator it = m_commandList; + QMutableListIterator it = m_commandList; while (it.hasNext()) { - GDBCommand* command = it.next(); + MICommand* command = it.next(); CommandType type = command->type(); if ((type >= VarEvaluateExpression && type <= VarListChildren) || type == VarUpdate) { if (command->flags() & (CmdImmediately | CmdInterrupt)) @@ -93,12 +92,12 @@ return m_immediatelyCounter > 0; } -GDBCommand* CommandQueue::nextCommand() +MICommand* CommandQueue::nextCommand() { if (m_commandList.isEmpty()) return nullptr; - GDBCommand* command = m_commandList.takeAt(0); + MICommand* command = m_commandList.takeAt(0); if (command->flags() & (CmdImmediately | CmdInterrupt)) --m_immediatelyCounter; diff --git a/debuggers/gdb/mi/milexer.h b/debuggers/common/mi/milexer.h rename from debuggers/gdb/mi/milexer.h rename to debuggers/common/mi/milexer.h --- a/debuggers/gdb/mi/milexer.h +++ b/debuggers/common/mi/milexer.h @@ -24,6 +24,8 @@ #include #include +namespace KDevMI { namespace MI { + class MILexer; struct TokenStream; @@ -143,5 +145,7 @@ tokenStream = 0; } +} // end of MI +} // end of KDevMI #endif diff --git a/debuggers/gdb/mi/milexer.cpp b/debuggers/common/mi/milexer.cpp rename from debuggers/gdb/mi/milexer.cpp rename to debuggers/common/mi/milexer.cpp --- a/debuggers/gdb/mi/milexer.cpp +++ b/debuggers/common/mi/milexer.cpp @@ -23,6 +23,8 @@ #include #include +using namespace KDevMI::MI; + bool MILexer::s_initialized = false; scan_fun_ptr MILexer::s_scan_table[]; diff --git a/debuggers/gdb/mi/miparser.h b/debuggers/common/mi/miparser.h rename from debuggers/gdb/mi/miparser.h rename to debuggers/common/mi/miparser.h --- a/debuggers/gdb/mi/miparser.h +++ b/debuggers/common/mi/miparser.h @@ -22,11 +22,13 @@ #include +#include "mi.h" #include "milexer.h" -#include "gdbmi.h" #include +namespace KDevMI { namespace MI { + /** @author Roberto Raggi */ @@ -36,17 +38,17 @@ MIParser(); ~MIParser(); - std::unique_ptr parse(FileSymbol *file); + std::unique_ptr parse(FileSymbol *file); protected: // rules - std::unique_ptr parseResultOrAsyncRecord(); - std::unique_ptr parsePrompt(); - std::unique_ptr parseStreamRecord(); + std::unique_ptr parseResultOrAsyncRecord(); + std::unique_ptr parsePrompt(); + std::unique_ptr parseStreamRecord(); - bool parseResult(GDBMI::Result *&result); - bool parseValue(GDBMI::Value *&value); - bool parseTuple(GDBMI::Value *&value); - bool parseList(GDBMI::Value *&value); + bool parseResult(Result *&result); + bool parseValue(Value *&value); + bool parseTuple(Value *&value); + bool parseList(Value *&value); /** Creates new TupleValue object, writes its address into *value, parses a comma-separated set of values, @@ -56,28 +58,28 @@ Parsing stops when we see 'end' character, or, if 'end' is zero, at the end of input. */ - bool parseCSV(GDBMI::TupleValue** value, + bool parseCSV(TupleValue** value, char start = 0, char end = 0); /** @overload Same as above, but writes into existing tuple. */ - bool parseCSV(GDBMI::TupleValue& value, + bool parseCSV(TupleValue& value, char start = 0, char end = 0); - /** Parses a string literal and returns it. Advances the lexer past the literal. Processes C escape sequences in the string. @pre lex->lookAhead(0) == Token_string_literal */ QString parseStringLiteral(); - - private: MILexer m_lexer; TokenStream *m_lex; }; +} // end of namespace MI +} // end of namespace KDevMI + #endif diff --git a/debuggers/gdb/mi/miparser.cpp b/debuggers/common/mi/miparser.cpp rename from debuggers/gdb/mi/miparser.cpp rename to debuggers/common/mi/miparser.cpp --- a/debuggers/gdb/mi/miparser.cpp +++ b/debuggers/common/mi/miparser.cpp @@ -22,7 +22,7 @@ #include "miparser.h" #include "tokens.h" -using namespace GDBMI; +using namespace KDevMI::MI; #define MATCH(tok) \ do { \ @@ -228,7 +228,7 @@ bool MIParser::parseTuple(Value *&value) { TupleValue* val; - + if (!parseCSV(&val, '{', '}')) return false; @@ -291,7 +291,7 @@ return true; } -bool MIParser::parseCSV(GDBMI::TupleValue& value, +bool MIParser::parseCSV(TupleValue& value, char start, char end) { if (start) @@ -321,7 +321,6 @@ return true; } - QString MIParser::parseStringLiteral() { QByteArray messageByteArray = m_lex->currentTokenText(); @@ -369,7 +368,7 @@ else { message2[target_index++] = message[i]; - } + } } m_lex->nextToken(); diff --git a/debuggers/gdb/mi/tokens.h b/debuggers/common/mi/tokens.h rename from debuggers/gdb/mi/tokens.h rename to debuggers/common/mi/tokens.h diff --git a/debuggers/gdb/breakpointcontroller.h b/debuggers/common/mibreakpointcontroller.h rename from debuggers/gdb/breakpointcontroller.h rename to debuggers/common/mibreakpointcontroller.h --- a/debuggers/gdb/breakpointcontroller.h +++ b/debuggers/common/mibreakpointcontroller.h @@ -19,80 +19,92 @@ the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef BREAKPOINTCONTROLLER_H -#define BREAKPOINTCONTROLLER_H +#ifndef MIBREAKPOINTCONTROLLER_H +#define MIBREAKPOINTCONTROLLER_H -#include +#include "dbgglobal.h" #include #include -#include "gdbglobal.h" +#include class QModelIndex; -namespace GDBMI { +namespace KDevMI { + +namespace MI { struct AsyncRecord; struct ResultRecord; struct Value; } -namespace GDBDebugger -{ -using namespace KDevelop; - -class DebugSession; -struct BreakpointData; +struct BreakpointData { + int debuggerId; + KDevelop::BreakpointModel::ColumnFlags dirty; + KDevelop::BreakpointModel::ColumnFlags sent; + KDevelop::BreakpointModel::ColumnFlags errors; + bool pending; + + BreakpointData() + : debuggerId(-1) + , pending(false) + {} +}; typedef QSharedPointer BreakpointDataPtr; +class MIDebugSession; /** * Handles signals from the editor that relate to breakpoints and the execution * point of the debugger. * We may change, add or remove breakpoints in this class. */ -class BreakpointController : public IBreakpointController +class MIBreakpointController : public KDevelop::IBreakpointController { Q_OBJECT public: - BreakpointController(DebugSession* parent); + MIBreakpointController( MIDebugSession* parent); using IBreakpointController::breakpointModel; - void initSendBreakpoints(); - /** * Controls whether when duplicate breakpoints are received via async notification from GDB, * one of the duplicates will be deleted. */ void setDeleteDuplicateBreakpoints(bool enable); - virtual void breakpointAdded(int row) override; - virtual void breakpointModelChanged(int row, BreakpointModel::ColumnFlags columns) override; - virtual void breakpointAboutToBeDeleted(int row) override; - virtual void debuggerStateChanged(IDebugSession::DebuggerState) override; + void breakpointAdded(int row) override; + void breakpointModelChanged(int row, KDevelop::BreakpointModel::ColumnFlags columns) override; + void breakpointAboutToBeDeleted(int row) override; + void debuggerStateChanged(KDevelop::IDebugSession::DebuggerState) override; - void notifyBreakpointCreated(const GDBMI::AsyncRecord& r); - void notifyBreakpointModified(const GDBMI::AsyncRecord& r); - void notifyBreakpointDeleted(const GDBMI::AsyncRecord& r); + void notifyBreakpointCreated(const MI::AsyncRecord& r); + void notifyBreakpointModified(const MI::AsyncRecord& r); + void notifyBreakpointDeleted(const MI::AsyncRecord& r); + +public Q_SLOTS: + void initSendBreakpoints(); private Q_SLOTS: - void programStopped(const GDBMI::AsyncRecord &r); + void programStopped(const MI::AsyncRecord &r); private: - DebugSession* debugSession() const; + MIDebugSession* debugSession() const; int breakpointRow(const BreakpointDataPtr& breakpoint); - void createGdbBreakpoint(int row); + void createBreakpoint(int row); void sendUpdates(int row); void recalculateState(int row); - virtual void sendMaybe(KDevelop::Breakpoint *breakpoint) override; + void sendMaybe(KDevelop::Breakpoint *breakpoint) override; - void createFromGdb(const GDBMI::Value& miBkpt); - void updateFromGdb(int row, const GDBMI::Value& miBkpt, BreakpointModel::ColumnFlags lockedColumns = 0); + // TODO: what's this + void createFromDebugger(const MI::Value& miBkpt); + void updateFromDebugger(int row, const MI::Value& miBkpt, + KDevelop::BreakpointModel::ColumnFlags lockedColumns = 0); - int rowFromGdbId(int gdbId) const; + int rowFromDebuggerId(int gdbId) const; struct Handler; struct InsertedHandler; @@ -106,6 +118,6 @@ bool m_deleteDuplicateBreakpoints = false; }; -} +} // end of namespace KDevMI -#endif +#endif // MIBREAKPOINTCONTROLLER_H diff --git a/debuggers/gdb/breakpointcontroller.cpp b/debuggers/common/mibreakpointcontroller.cpp rename from debuggers/gdb/breakpointcontroller.cpp rename to debuggers/common/mibreakpointcontroller.cpp --- a/debuggers/gdb/breakpointcontroller.cpp +++ b/debuggers/common/mibreakpointcontroller.cpp @@ -21,55 +21,28 @@ Boston, MA 02111-1307, USA. */ -#include "breakpointcontroller.h" +#include "mibreakpointcontroller.h" -#include +#include "debuglog.h" +#include "midebugsession.h" +#include "mi/micommand.h" +#include "stringhelpers.h" +#include +#include #include #include -#include -#include - -#include "gdbcommand.h" -#include "debugsession.h" -#include "debug.h" - -using namespace GDBMI; - -namespace GDBDebugger { - -QString quoteExpression(QString expr) -{ - expr.replace('"', "\\\""); - expr = expr.prepend('"').append('"'); - return expr; -} -QString unquoteExpression(QString expr) -{ - if (expr.left(1) == QString('"') && expr.right(1) == QString('"')) { - expr = expr.mid(1, expr.length()-2); - expr.replace("\\\"", "\""); - } - return expr; -} +#include -struct BreakpointData { - int gdbId; - BreakpointModel::ColumnFlags dirty; - BreakpointModel::ColumnFlags sent; - BreakpointModel::ColumnFlags errors; - bool pending; - - BreakpointData() - : gdbId(-1) - , pending(false) - {} -}; +using namespace KDevMI; +using namespace KDevMI::MI; +using namespace KDevelop; -struct BreakpointController::Handler : public GDBCommandHandler +struct MIBreakpointController::Handler : public MICommandHandler { - Handler(BreakpointController* controller, const BreakpointDataPtr& b, BreakpointModel::ColumnFlags columns) + Handler(MIBreakpointController* controller, const BreakpointDataPtr& b, + BreakpointModel::ColumnFlags columns) : controller(controller) , breakpoint(b) , columns(columns) @@ -109,17 +82,18 @@ return true; } - BreakpointController* controller; + MIBreakpointController* controller; BreakpointDataPtr breakpoint; BreakpointModel::ColumnFlags columns; }; -struct BreakpointController::UpdateHandler : public BreakpointController::Handler +struct MIBreakpointController::UpdateHandler : public MIBreakpointController::Handler { - UpdateHandler(BreakpointController* c, const BreakpointDataPtr& b, BreakpointModel::ColumnFlags columns) + UpdateHandler(MIBreakpointController* c, const BreakpointDataPtr& b, + BreakpointModel::ColumnFlags columns) : Handler(c, b, columns) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { Handler::handle(r); @@ -135,12 +109,13 @@ } }; -struct BreakpointController::InsertedHandler : public BreakpointController::Handler +struct MIBreakpointController::InsertedHandler : public MIBreakpointController::Handler { - InsertedHandler(BreakpointController* c, const BreakpointDataPtr& b, BreakpointModel::ColumnFlags columns) + InsertedHandler(MIBreakpointController* c, const BreakpointDataPtr& b, + BreakpointModel::ColumnFlags columns) : Handler(c, b, columns) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { Handler::handle(r); @@ -161,16 +136,16 @@ const Value& miBkpt = r[bkptKind]; - breakpoint->gdbId = miBkpt["number"].toInt(); + breakpoint->debuggerId = miBkpt["number"].toInt(); if (row >= 0) { - controller->updateFromGdb(row, miBkpt); + controller->updateFromDebugger(row, miBkpt); if (breakpoint->dirty != 0) controller->sendUpdates(row); } else { // breakpoint was deleted while insertion was in flight controller->debugSession()->addCommand( - new GDBCommand(BreakDelete, QString::number(breakpoint->gdbId), + new MICommand(BreakDelete, QString::number(breakpoint->debuggerId), CmdImmediately)); } } @@ -181,18 +156,18 @@ } }; -struct BreakpointController::DeleteHandler : BreakpointController::Handler { - DeleteHandler(BreakpointController* c, const BreakpointDataPtr& b) +struct MIBreakpointController::DeleteHandler : MIBreakpointController::Handler { + DeleteHandler(MIBreakpointController* c, const BreakpointDataPtr& b) : Handler(c, b, 0) {} void handle(const ResultRecord&) override { controller->m_pendingDeleted.removeAll(breakpoint); } }; -struct BreakpointController::IgnoreChanges { - IgnoreChanges(BreakpointController& controller) +struct MIBreakpointController::IgnoreChanges { + IgnoreChanges(MIBreakpointController& controller) : controller(controller) { ++controller.m_ignoreChanges; @@ -203,47 +178,48 @@ --controller.m_ignoreChanges; } - BreakpointController& controller; + MIBreakpointController& controller; }; -BreakpointController::BreakpointController(DebugSession* parent) +MIBreakpointController::MIBreakpointController(MIDebugSession * parent) : IBreakpointController(parent) { Q_ASSERT(parent); - connect(parent, &DebugSession::programStopped, this, &BreakpointController::programStopped); + connect(parent, &MIDebugSession::inferiorStopped, + this, &MIBreakpointController::programStopped); int numBreakpoints = breakpointModel()->breakpoints().size(); for (int row = 0; row < numBreakpoints; ++row) breakpointAdded(row); } -DebugSession *BreakpointController::debugSession() const +MIDebugSession *MIBreakpointController::debugSession() const { Q_ASSERT(QObject::parent()); - return static_cast(const_cast(QObject::parent())); + return static_cast(const_cast(QObject::parent())); } -int BreakpointController::breakpointRow(const BreakpointDataPtr& breakpoint) +int MIBreakpointController::breakpointRow(const BreakpointDataPtr& breakpoint) { return m_breakpoints.indexOf(breakpoint); } -void BreakpointController::setDeleteDuplicateBreakpoints(bool enable) +void MIBreakpointController::setDeleteDuplicateBreakpoints(bool enable) { m_deleteDuplicateBreakpoints = enable; } -void BreakpointController::initSendBreakpoints() +void MIBreakpointController::initSendBreakpoints() { for (int row = 0; row < m_breakpoints.size(); ++row) { BreakpointDataPtr breakpoint = m_breakpoints[row]; - if (breakpoint->gdbId < 0 && breakpoint->sent == 0) { - createGdbBreakpoint(row); + if (breakpoint->debuggerId < 0 && breakpoint->sent == 0) { + createBreakpoint(row); } } } -void BreakpointController::breakpointAdded(int row) +void MIBreakpointController::breakpointAdded(int row) { if (m_ignoreChanges > 0) return; @@ -261,10 +237,10 @@ if (!modelBreakpoint->address().isEmpty()) breakpoint->dirty |= BreakpointModel::LocationColumnFlag; - createGdbBreakpoint(row); + createBreakpoint(row); } -void BreakpointController::breakpointModelChanged(int row, BreakpointModel::ColumnFlags columns) +void MIBreakpointController::breakpointModelChanged(int row, BreakpointModel::ColumnFlags columns) { if (m_ignoreChanges > 0) return; @@ -281,41 +257,42 @@ return; } - if (breakpoint->gdbId < 0) { - createGdbBreakpoint(row); + if (breakpoint->debuggerId < 0) { + createBreakpoint(row); } else { sendUpdates(row); } } -void BreakpointController::breakpointAboutToBeDeleted(int row) +void MIBreakpointController::breakpointAboutToBeDeleted(int row) { if (m_ignoreChanges > 0) return; BreakpointDataPtr breakpoint = m_breakpoints.at(row); m_breakpoints.removeAt(row); - if (breakpoint->gdbId < 0) { + if (breakpoint->debuggerId < 0) { // Two possibilities: // (1) Breakpoint has never been sent to GDB, so we're done // (2) Breakpoint has been sent to GDB, but we haven't received // the response yet; the response handler will delete the // breakpoint. return; } - if (debugSession()->stateIsOn(s_dbgNotStarted)) + if (debugSession()->debuggerStateIsOn(s_dbgNotStarted)) return; debugSession()->addCommand( - new GDBCommand( - BreakDelete, QString::number(breakpoint->gdbId), + new MICommand( + BreakDelete, QString::number(breakpoint->debuggerId), new DeleteHandler(this, breakpoint), CmdImmediately)); m_pendingDeleted << breakpoint; } -void BreakpointController::debuggerStateChanged(IDebugSession::DebuggerState state) +// Note: despite the name, this is in fact session state changed. +void MIBreakpointController::debuggerStateChanged(IDebugSession::DebuggerState state) { IgnoreChanges ignoreChanges(*this); if (state == IDebugSession::EndedState || @@ -330,15 +307,15 @@ } } -void BreakpointController::createGdbBreakpoint(int row) +void MIBreakpointController::createBreakpoint(int row) { - if (debugSession()->stateIsOn(s_dbgNotStarted)) + if (debugSession()->debuggerStateIsOn(s_dbgNotStarted)) return; BreakpointDataPtr breakpoint = m_breakpoints.at(row); Breakpoint* modelBreakpoint = breakpointModel()->breakpoint(row); - Q_ASSERT(breakpoint->gdbId < 0 && breakpoint->sent == 0); + Q_ASSERT(breakpoint->debuggerId < 0 && breakpoint->sent == 0); if (modelBreakpoint->location().isEmpty()) return; @@ -357,23 +334,23 @@ location = "exception throw"; } - // Note: We rely on '-f' to be automatically added by the GDBCommand logic + // Note: We rely on '-f' to be automatically added by the MICommand logic QString arguments; if (!modelBreakpoint->enabled()) arguments += "-d "; if (!modelBreakpoint->condition().isEmpty()) - arguments += QString("-c %0 ").arg(quoteExpression(modelBreakpoint->condition())); + arguments += QString("-c %0 ").arg(Utils::quoteExpression(modelBreakpoint->condition())); if (modelBreakpoint->ignoreHits() != 0) arguments += QString("-i %0 ").arg(modelBreakpoint->ignoreHits()); - arguments += quoteExpression(location); + arguments += Utils::quoteExpression(location); BreakpointModel::ColumnFlags sent = BreakpointModel::EnableColumnFlag | BreakpointModel::ConditionColumnFlag | BreakpointModel::IgnoreHitsColumnFlag | BreakpointModel::LocationColumnFlag; debugSession()->addCommand( - new GDBCommand(BreakInsert, arguments, + new MICommand(BreakInsert, arguments, new InsertedHandler(this, breakpoint, sent), CmdImmediately)); } else { @@ -384,61 +361,61 @@ opt = "-a "; debugSession()->addCommand( - new GDBCommand( + new MICommand( BreakWatch, - opt + quoteExpression(modelBreakpoint->location()), + opt + Utils::quoteExpression(modelBreakpoint->location()), new InsertedHandler(this, breakpoint, BreakpointModel::LocationColumnFlag), CmdImmediately)); } recalculateState(row); } -void BreakpointController::sendUpdates(int row) +void MIBreakpointController::sendUpdates(int row) { - if (debugSession()->stateIsOn(s_dbgNotStarted)) + if (debugSession()->debuggerStateIsOn(s_dbgNotStarted)) return; BreakpointDataPtr breakpoint = m_breakpoints.at(row); Breakpoint* modelBreakpoint = breakpointModel()->breakpoint(row); - Q_ASSERT(breakpoint->gdbId >= 0 && breakpoint->sent == 0); + Q_ASSERT(breakpoint->debuggerId >= 0 && breakpoint->sent == 0); if (breakpoint->dirty & BreakpointModel::LocationColumnFlag) { // Gdb considers locations as fixed, so delete and re-create the breakpoint debugSession()->addCommand( - new GDBCommand(BreakDelete, QString::number(breakpoint->gdbId), CmdImmediately)); - breakpoint->gdbId = -1; - createGdbBreakpoint(row); + new MICommand(BreakDelete, QString::number(breakpoint->debuggerId), CmdImmediately)); + breakpoint->debuggerId = -1; + createBreakpoint(row); return; } if (breakpoint->dirty & BreakpointModel::EnableColumnFlag) { debugSession()->addCommand( - new GDBCommand(modelBreakpoint->enabled() ? BreakEnable : BreakDisable, - QString::number(breakpoint->gdbId), + new MICommand(modelBreakpoint->enabled() ? BreakEnable : BreakDisable, + QString::number(breakpoint->debuggerId), new UpdateHandler(this, breakpoint, BreakpointModel::EnableColumnFlag), CmdImmediately)); } if (breakpoint->dirty & BreakpointModel::IgnoreHitsColumnFlag) { debugSession()->addCommand( - new GDBCommand(BreakAfter, - QString("%0 %1").arg(breakpoint->gdbId).arg(modelBreakpoint->ignoreHits()), + new MICommand(BreakAfter, + QString("%0 %1").arg(breakpoint->debuggerId).arg(modelBreakpoint->ignoreHits()), new UpdateHandler(this, breakpoint, BreakpointModel::IgnoreHitsColumnFlag), CmdImmediately)); } if (breakpoint->dirty & BreakpointModel::ConditionColumnFlag) { debugSession()->addCommand( - new GDBCommand(BreakCondition, - QString("%0 %1").arg(breakpoint->gdbId).arg(modelBreakpoint->condition()), + new MICommand(BreakCondition, + QString("%0 %1").arg(breakpoint->debuggerId).arg(modelBreakpoint->condition()), new UpdateHandler(this, breakpoint, BreakpointModel::ConditionColumnFlag), CmdImmediately)); } recalculateState(row); } -void BreakpointController::recalculateState(int row) +void MIBreakpointController::recalculateState(int row) { BreakpointDataPtr breakpoint = m_breakpoints.at(row); @@ -448,7 +425,7 @@ Breakpoint::BreakpointState newState = Breakpoint::NotStartedState; if (debugSession()->state() != IDebugSession::EndedState && debugSession()->state() != IDebugSession::NotStartedState) { - if (!debugSession()->stateIsOn(s_dbgNotStarted)) { + if (!debugSession()->debuggerStateIsOn(s_dbgNotStarted)) { if (breakpoint->dirty == 0 && breakpoint->sent == 0) { if (breakpoint->pending) { newState = Breakpoint::PendingState; @@ -464,16 +441,16 @@ updateState(row, newState); } -int BreakpointController::rowFromGdbId(int gdbId) const +int MIBreakpointController::rowFromDebuggerId(int gdbId) const { for (int row = 0; row < m_breakpoints.size(); ++row) { - if (gdbId == m_breakpoints[row]->gdbId) + if (gdbId == m_breakpoints[row]->debuggerId) return row; } return -1; } -void BreakpointController::notifyBreakpointCreated(const AsyncRecord& r) +void MIBreakpointController::notifyBreakpointCreated(const AsyncRecord& r) { const Value& miBkpt = r["bkpt"]; @@ -486,35 +463,35 @@ if (miBkpt["number"].literal().contains('.')) return; - createFromGdb(miBkpt); + createFromDebugger(miBkpt); } -void BreakpointController::notifyBreakpointModified(const AsyncRecord& r) +void MIBreakpointController::notifyBreakpointModified(const AsyncRecord& r) { const Value& miBkpt = r["bkpt"]; const int gdbId = miBkpt["number"].toInt(); - const int row = rowFromGdbId(gdbId); + const int row = rowFromDebuggerId(gdbId); if (row < 0) { for (const auto& breakpoint : m_pendingDeleted) { - if (breakpoint->gdbId == gdbId) { + if (breakpoint->debuggerId == gdbId) { // Received a modification of a breakpoint whose deletion is currently // in-flight; simply ignore it. return; } } - qWarning() << "Received a modification of an unknown breakpoint"; - createFromGdb(miBkpt); + qCWarning(DEBUGGERCOMMON) << "Received a modification of an unknown breakpoint"; + createFromDebugger(miBkpt); } else { - updateFromGdb(row, miBkpt); + updateFromDebugger(row, miBkpt); } } -void BreakpointController::notifyBreakpointDeleted(const AsyncRecord& r) +void MIBreakpointController::notifyBreakpointDeleted(const AsyncRecord& r) { const int gdbId = r["id"].toInt(); - const int row = rowFromGdbId(gdbId); + const int row = rowFromDebuggerId(gdbId); if (row < 0) { // The user may also have deleted the breakpoint via the UI simultaneously @@ -526,7 +503,7 @@ m_breakpoints.removeAt(row); } -void BreakpointController::createFromGdb(const Value& miBkpt) +void MIBreakpointController::createFromDebugger(const Value& miBkpt) { const QString type = miBkpt["type"].literal(); Breakpoint::BreakpointKind gdbKind; @@ -539,17 +516,17 @@ } else if (type == "acc watchpoint") { gdbKind = Breakpoint::AccessBreakpoint; } else { - qWarning() << "Unknown gdb breakpoint type " << type; + qCWarning(DEBUGGERCOMMON) << "Unknown breakpoint type " << type; return; } - // During gdb startup, we want to avoid creating duplicate breakpoints when the same breakpoint - // appears both in our model and in a .gdbinit file + // During debugger startup, we want to avoid creating duplicate breakpoints when the same breakpoint + // appears both in our model and in a init file e.g. .gdbinit BreakpointModel* model = breakpointModel(); const int numRows = model->rowCount(); for (int row = 0; row < numRows; ++row) { BreakpointDataPtr breakpoint = m_breakpoints.at(row); - const bool breakpointSent = breakpoint->gdbId >= 0 || breakpoint->sent != 0; + const bool breakpointSent = breakpoint->debuggerId >= 0 || breakpoint->sent != 0; if (breakpointSent && !m_deleteDuplicateBreakpoints) continue; @@ -561,7 +538,7 @@ bool sameLocation = false; if (miBkpt.hasField("fullname") && miBkpt.hasField("line")) { - const QString location = unquoteExpression(miBkpt["fullname"].literal()); + const QString location = Utils::unquoteExpression(miBkpt["fullname"].literal()); const int line = miBkpt["line"].toInt() - 1; if (location == modelBreakpoint->url().url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash) && line == modelBreakpoint->line()) @@ -577,7 +554,7 @@ } else { QRegExp rx("^(.+):(\\d+)$"); if (rx.indexIn(location) != -1 && - unquoteExpression(rx.cap(1)) == modelBreakpoint->url().url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash) && + Utils::unquoteExpression(rx.cap(1)) == modelBreakpoint->url().url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash) && rx.cap(2).toInt() - 1 == modelBreakpoint->line()) { sameLocation = true; } @@ -594,7 +571,7 @@ if (!sameLocation) continue; } else { - if (unquoteExpression(miBkpt["original-location"].literal()) != modelBreakpoint->expression()) { + if (Utils::unquoteExpression(miBkpt["original-location"].literal()) != modelBreakpoint->expression()) { continue; } } @@ -608,7 +585,7 @@ // Breakpoint is equivalent if (!breakpointSent) { - breakpoint->gdbId = miBkpt["number"].toInt(); + breakpoint->debuggerId = miBkpt["number"].toInt(); // Reasonable people can probably have different opinions about what the "correct" behavior // should be for the "enabled" and "ignore hits" column. @@ -625,7 +602,7 @@ if (gdbIgnoreHits != modelBreakpoint->ignoreHits()) breakpoint->dirty |= BreakpointModel::IgnoreHitsColumnFlag; - updateFromGdb(row, miBkpt, BreakpointModel::EnableColumnFlag | BreakpointModel::IgnoreHitsColumnFlag); + updateFromDebugger(row, miBkpt, BreakpointModel::EnableColumnFlag | BreakpointModel::IgnoreHitsColumnFlag); return; } @@ -658,18 +635,18 @@ // Since we are in ignore-changes mode, we have to add the BreakpointData manually. auto breakpoint = BreakpointDataPtr::create(); m_breakpoints << breakpoint; - breakpoint->gdbId = miBkpt["number"].toInt(); + breakpoint->debuggerId = miBkpt["number"].toInt(); - updateFromGdb(row, miBkpt); + updateFromDebugger(row, miBkpt); } // This method is required for the legacy interface which will be removed -void BreakpointController::sendMaybe(KDevelop::Breakpoint*) +void MIBreakpointController::sendMaybe(KDevelop::Breakpoint*) { Q_ASSERT(false); } -void BreakpointController::updateFromGdb(int row, const Value& miBkpt, BreakpointModel::ColumnFlags lockedColumns) +void MIBreakpointController::updateFromDebugger(int row, const Value& miBkpt, BreakpointModel::ColumnFlags lockedColumns) { IgnoreChanges ignoreChanges(*this); BreakpointDataPtr breakpoint = m_breakpoints[row]; @@ -687,15 +664,16 @@ // We try to do the best we can until the breakpoint model gets cleaned up. if (miBkpt.hasField("fullname") && miBkpt.hasField("line")) { modelBreakpoint->setLocation( - QUrl::fromLocalFile(unquoteExpression(miBkpt["fullname"].literal())), + QUrl::fromLocalFile(Utils::unquoteExpression(miBkpt["fullname"].literal())), miBkpt["line"].toInt() - 1); } else if (miBkpt.hasField("original-location")) { QRegExp rx("^(.+):(\\d+)$"); QString location = miBkpt["original-location"].literal(); if (rx.indexIn(location) != -1) { - modelBreakpoint->setLocation(QUrl::fromLocalFile(unquoteExpression(rx.cap(1))), rx.cap(2).toInt()-1); + modelBreakpoint->setLocation(QUrl::fromLocalFile(Utils::unquoteExpression(rx.cap(1))), + rx.cap(2).toInt()-1); } else { - modelBreakpoint->setData(Breakpoint::LocationColumn, unquoteExpression(location)); + modelBreakpoint->setData(Breakpoint::LocationColumn, Utils::unquoteExpression(location)); } } else if (miBkpt.hasField("what")) { modelBreakpoint->setExpression(miBkpt["what"].literal()); @@ -745,28 +723,28 @@ recalculateState(row); } -void BreakpointController::programStopped(const GDBMI::AsyncRecord& r) +void MIBreakpointController::programStopped(const AsyncRecord& r) { if (!r.hasField("reason")) return; const QString reason = r["reason"].literal(); - int gdbId = -1; + int debuggerId = -1; if (reason == "breakpoint-hit") { - gdbId = r["bkptno"].toInt(); + debuggerId = r["bkptno"].toInt(); } else if (reason == "watchpoint-trigger") { - gdbId = r["wpt"]["number"].toInt(); + debuggerId = r["wpt"]["number"].toInt(); } else if (reason == "read-watchpoint-trigger") { - gdbId = r["hw-rwpt"]["number"].toInt(); + debuggerId = r["hw-rwpt"]["number"].toInt(); } else if (reason == "access-watchpoint-trigger") { - gdbId = r["hw-awpt"]["number"].toInt(); + debuggerId = r["hw-awpt"]["number"].toInt(); } - if (gdbId < 0) + if (debuggerId < 0) return; - int row = rowFromGdbId(gdbId); + int row = rowFromDebuggerId(debuggerId); if (row < 0) return; @@ -782,5 +760,3 @@ notifyHit(row, msg); } - -} diff --git a/debuggers/gdb/gdb.h b/debuggers/common/midebugger.h copy from debuggers/gdb/gdb.h copy to debuggers/common/midebugger.h --- a/debuggers/gdb/gdb.h +++ b/debuggers/common/midebugger.h @@ -1,7 +1,8 @@ /* - * Low level GDB interface. + * 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 @@ -19,92 +20,96 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef GDB_H_d5c9cb274cbad688fe7a507a84f6633b -#define GDB_H_d5c9cb274cbad688fe7a507a84f6633b +#ifndef MIDEBUGGER_H +#define MIDEBUGGER_H -#include "mi/gdbmi.h" +#include "mi/mi.h" #include "mi/miparser.h" -#include "gdbcommand.h" #include -#include #include +#include class KConfigGroup; +class KProcess; -namespace GDBDebugger -{ +namespace KDevMI { + +namespace MI { +class MICommand; +} -class GDB : public QObject + +class MIDebugger : public QObject { Q_OBJECT public: - explicit GDB(QObject* parent = 0); - ~GDB() override; + explicit MIDebugger(QObject* parent = 0); + ~MIDebugger() override; - /** Starts GDB. This should be done after connecting to all + /** Starts the debugger. This should be done after connecting to all signals the client is interested in. */ - void start(KConfigGroup& config, const QStringList& extraArguments = {}); + virtual void 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 - GDB instance is just constructed, one should wait - for 'ready' as well. + debugger instance is just constructed, one should wait + for 'ready' as well. - The ownership of 'command' is transferred to GDB. */ - void execute(GDBCommand* command); + 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. */ - GDBCommand* currentCommand() const; - - /** Arrange to gdb to stop doing whatever it's doing, - and start waiting for a command. + 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 GDB. */ + /** Kills the debugger. */ void kill(); Q_SIGNALS: /** Emitted when debugger becomes ready -- i.e. when isReady call will return true. */ void ready(); - /** Emitted when GDB itself exits. This could happen because + /** Emitted when the debugger itself exits. This could happen because it just crashed due to internal bug, or we killed it explicitly. */ - void gdbExited(); + void exited(bool abnormal, const QString &msg); - /** Emitted when GDB reports stop, with 'r' being the - data provided by GDB. */ - void programStopped(const GDBMI::AsyncRecord& r); - - /** Emitted when GDB believes that the program is running. */ + /** 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 GDBMI::StreamRecord& s); + void streamRecord(const MI::StreamRecord& s); /** Reports an async notification record. */ - void notification(const GDBMI::AsyncRecord& n); - + void notification(const MI::AsyncRecord& n); + /** Emitted for error that is not handled by the command being executed. */ - void error(const GDBMI::ResultRecord& s); + void error(const MI::ResultRecord& s); /** Reports output from the running application. Generally output will only be available when - using remote GDB targets. When running locally, - the output will either appear on GDB stdout, and + 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); @@ -114,27 +119,27 @@ /** Reports output of a command issued internally by KDevelop. At the moment, stderr output from - GDB and the 'log' MI channel will be also routed here. */ + debugger and the 'log' MI channel will be also routed here. */ void internalCommandOutput(const QString& s); -private Q_SLOTS: +protected Q_SLOTS: void readyReadStandardOutput(); void readyReadStandardError(); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void processErrored(QProcess::ProcessError); -private: +protected: void processLine(const QByteArray& line); -private: - QString gdbBinary_; +protected: + QString debuggerBinary_; KProcess* process_; - GDBCommand* currentCmd_; + MI::MICommand* currentCmd_; - MIParser mi_parser_; + MI::MIParser mi_parser_; - /** The unprocessed output from gdb. Output is + /** The unprocessed output from debugger. Output is processed as soon as we see newline. */ QByteArray buffer_; }; diff --git a/debuggers/gdb/gdb.cpp b/debuggers/common/midebugger.cpp copy from debuggers/gdb/gdb.cpp copy to debuggers/common/midebugger.cpp --- a/debuggers/gdb/gdb.cpp +++ b/debuggers/common/midebugger.cpp @@ -3,6 +3,7 @@ * * 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 @@ -20,117 +21,62 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "gdb.h" -#include "debugsession.h" -#include "debug.h" +#include "midebugger.h" + +#include "debuglog.h" +#include "mi/micommand.h" + +#include "sys/signal.h" -#include -#include -#include -#include #include +#include #include -#include +#include +#include #include -#include -#include - // #define DEBUG_NO_TRY //to get a backtrace to where the exception was thrown -Q_LOGGING_CATEGORY(DEBUGGERGDB, "kdevelop.debuggers.gdb") - -using namespace GDBDebugger; +using namespace KDevMI; +using namespace KDevMI::MI; -GDB::GDB(QObject* parent) -: QObject(parent), process_(0), currentCmd_(0) +MIDebugger::MIDebugger(QObject* parent) + : QObject(parent) + , process_(nullptr) + , currentCmd_(nullptr) { + process_ = new KProcess(this); + process_->setOutputChannelMode(KProcess::SeparateChannels); + connect(process_, &KProcess::readyReadStandardOutput, + this, &MIDebugger::readyReadStandardOutput); + connect(process_, &KProcess::readyReadStandardError, + this, &MIDebugger::readyReadStandardError); + connect(process_, + static_cast(&KProcess::finished), + this, &MIDebugger::processFinished); + connect(process_, static_cast(&KProcess::error), + this, &MIDebugger::processErrored); } -GDB::~GDB() +MIDebugger::~MIDebugger() { // prevent Qt warning: QProcess: Destroyed while process is still running. if (process_ && process_->state() == QProcess::Running) { disconnect(process_, static_cast(&KProcess::error), - this, &GDB::processErrored); + this, &MIDebugger::processErrored); process_->kill(); process_->waitForFinished(10); } } -void GDB::start(KConfigGroup& config, const QStringList& extraArguments) -{ - // FIXME: verify that default value leads to something sensible - QUrl gdbUrl = config.readEntry(GDBDebugger::gdbPathEntry, QUrl()); - if (gdbUrl.isEmpty()) { - gdbBinary_ = "gdb"; - } else { - // FIXME: verify its' a local path. - gdbBinary_ = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); - } - process_ = new KProcess(this); - process_->setOutputChannelMode( KProcess::SeparateChannels ); - connect(process_, &KProcess::readyReadStandardOutput, - this, &GDB::readyReadStandardOutput); - connect(process_, &KProcess::readyReadStandardError, - this, &GDB::readyReadStandardError); - connect(process_, - static_cast(&KProcess::finished), - this, &GDB::processFinished); - connect(process_, static_cast(&KProcess::error), - this, &GDB::processErrored); - - - QStringList arguments = extraArguments; - arguments << "--interpreter=mi2" << "-quiet"; - - QUrl shell = config.readEntry(GDBDebugger::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, gdbBinary_); - arguments.insert(0, shell.toLocalFile()); - process_->setShellCommand( KShell::joinArgs( arguments ) ); - } - else - { - process_->setProgram( gdbBinary_, arguments ); - } - - process_->start(); - - qCDebug(DEBUGGERGDB) << "STARTING GDB\n"; - emit userCommandOutput(shell.toLocalFile() + ' ' + gdbBinary_ - + " --interpreter=mi2 -quiet\n" ); -} - - -void GDB::execute(GDBCommand* command) +void MIDebugger::execute(MICommand* command) { currentCmd_ = command; QString commandText = currentCmd_->cmdToSend(); - qCDebug(DEBUGGERGDB) << "SEND:" << commandText.trimmed(); + qCDebug(DEBUGGERCOMMON) << "SEND:" << commandText.trimmed(); QByteArray commandUtf8 = commandText.toUtf8(); @@ -146,31 +92,31 @@ emit internalCommandOutput(prettyCmd); } -bool GDB::isReady() const +bool MIDebugger::isReady() const { return currentCmd_ == 0; } -void GDB::interrupt() +void MIDebugger::interrupt() { //TODO:win32 Porting needed int pid = process_->pid(); if (pid != 0) { ::kill(pid, SIGINT); } } -GDBCommand* GDB::currentCommand() const +MICommand* MIDebugger::currentCommand() const { return currentCmd_; } -void GDB::kill() +void MIDebugger::kill() { process_->kill(); } -void GDB::readyReadStandardOutput() +void MIDebugger::readyReadStandardOutput() { process_->setReadChannel(QProcess::StandardOutput); @@ -189,25 +135,25 @@ } } -void GDB::readyReadStandardError() +void MIDebugger::readyReadStandardError() { process_->setReadChannel(QProcess::StandardOutput); emit internalCommandOutput(QString::fromUtf8(process_->readAll())); } -void GDB::processLine(const QByteArray& line) +void MIDebugger::processLine(const QByteArray& line) { - qCDebug(DEBUGGERGDB) << "GDB output: " << line; + qCDebug(DEBUGGERCOMMON) << "Debugger (" << process_->pid() <<") output: " << line; FileSymbol file; file.contents = line; - std::unique_ptr r(mi_parser_.parse(&file)); + std::unique_ptr r(mi_parser_.parse(&file)); if (!r) { // FIXME: Issue an error! - qCDebug(DEBUGGERGDB) << "Invalid MI message:" << line; + 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 @@ -224,50 +170,51 @@ #endif switch(r->kind) { - case GDBMI::Record::Result: { - GDBMI::ResultRecord& result = static_cast(*r); + 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(DEBUGGERGDB) << "Received a result without a pending command"; + 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(DEBUGGERGDB) << "Handling error"; + qCDebug(DEBUGGERCOMMON) << "Handling error"; // Some commands want to handle errors themself. if (currentCmd_->handlesError() && currentCmd_->invokeHandler(result)) { - qCDebug(DEBUGGERGDB) << "Invoked custom handler\n"; + qCDebug(DEBUGGERCOMMON) << "Invoked custom handler\n"; // Done, nothing more needed } else emit error(result); } else { - qCDebug(DEBUGGERGDB) << "Unhandled result code: " << result.reason; + qCDebug(DEBUGGERCOMMON) << "Unhandled result code: " << result.reason; } delete currentCmd_; currentCmd_ = nullptr; emit ready(); break; } - case GDBMI::Record::Async: { - GDBMI::AsyncRecord& async = dynamic_cast(*r); + case MI::Record::Async: { + MI::AsyncRecord& async = dynamic_cast(*r); switch (async.subkind) { - case GDBMI::AsyncRecord::Exec: { + case MI::AsyncRecord::Exec: { // Prefix '*'; asynchronous state changes of the target if (async.reason == "stopped") { @@ -279,18 +226,18 @@ } else { - qCDebug(DEBUGGERGDB) << "Unhandled exec notification: " << async.reason; + qCDebug(DEBUGGERCOMMON) << "Unhandled exec notification: " << async.reason; } break; } - case GDBMI::AsyncRecord::Notify: { + case MI::AsyncRecord::Notify: { // Prefix '='; supplementary information that we should handle (new breakpoint etc.) emit notification(async); break; } - case GDBMI::AsyncRecord::Status: { + case MI::AsyncRecord::Status: { // Prefix '+'; GDB documentation: // On-going status information about progress of a slow operation; may be ignored break; @@ -302,16 +249,16 @@ break; } - case GDBMI::Record::Stream: { + case MI::Record::Stream: { - GDBMI::StreamRecord& s = dynamic_cast(*r); + MI::StreamRecord& s = dynamic_cast(*r); - if (s.subkind == GDBMI::StreamRecord::Target) { + if (s.subkind == MI::StreamRecord::Target) { emit applicationOutput(s.message); } else { if (currentCmd_ && currentCmd_->isUserCommand()) emit userCommandOutput(s.message); - else if (s.subkind == GDBMI::StreamRecord::Console) { + else if (s.subkind == MI::StreamRecord::Console) { emit applicationOutput(s.message); } else { emit internalCommandOutput(s.message); @@ -326,7 +273,7 @@ break; } - case GDBMI::Record::Prompt: + case MI::Record::Prompt: break; } #ifndef DEBUG_NO_TRY @@ -348,65 +295,40 @@ // ************************************************************************** -void GDB::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +void MIDebugger::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { - Q_UNUSED(exitCode); - Q_UNUSED(exitStatus); - qCDebug(DEBUGGERGDB) << "GDB FINISHED\n"; - /* FIXME: return the status? */ - emit gdbExited(); - + qCDebug(DEBUGGERCOMMON) << "GDB FINISHED\n"; -/* FIXME: revive. Need to arrange for controller to delete us. bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit; - deleteLater(); - delete tty_; - tty_ = 0; - - if (abnormal) - emit debuggerAbnormalExit(); - - raiseEvent(debugger_exited); - - destroyCmds(); - setState(s_dbgNotStarted|s_appNotStarted|s_programExited); - emit showMessage(i18n("Process exited"), 3000); - - emit gdbUserCommandStdout("(gdb) Process exited\n"); -*/ + emit userCommandOutput("(gdb) Process exited\n"); + emit exited(abnormal, i18n("Process exited")); } -void GDB::processErrored(QProcess::ProcessError error) +void MIDebugger::processErrored(QProcess::ProcessError error) { - qCDebug(DEBUGGERGDB) << "GDB ERRORED" << 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.", - gdbBinary_), + debuggerBinary_), i18n("Could not start debugger")); - /* FIXME: make sure the controller gets rids of GDB instance - emit debuggerAbnormalExit(); - - raiseEvent(debugger_exited); */ - - /* Used to be before, GDB controller might want to do - the same. - destroyCmds(); - setState(s_dbgNotStarted|s_appNotStarted|s_programExited); - emit showMessage(i18n("Process didn't start"), 3000); - */ 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/common/midebugsession.h b/debuggers/common/midebugsession.h new file mode 100644 --- /dev/null +++ b/debuggers/common/midebugsession.h @@ -0,0 +1,317 @@ +/* + * Common code for debugger support + * + * Copyright 1999-2001 John Birch + * Copyright 2001 by Bernd Gehrmann + * Copyright 2007 Hamish Rodda + * Copyright 2009 Niko Sams + * 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 . + * + */ + +#ifndef MIDEBUGSESSION_H +#define MIDEBUGSESSION_H + +#include + +#include "dbgglobal.h" +#include "mibreakpointcontroller.h" +#include "mi/mi.h" + +#include + +class IExecutePlugin; +namespace KDevelop { +class ILaunchConfiguration; +class ProcessLineMaker; +} + +namespace KDevMI { + +namespace MI { +class CommandQueue; +class MICommand; +} + +class MIDebugger; +class STTY; +class MIDebugSession : public KDevelop::IDebugSession +{ + Q_OBJECT +public: + MIDebugSession(); + ~MIDebugSession() override; + +Q_SIGNALS: + /** + * Emits when received standard output lines from inferior + */ + void inferiorStdoutLines(const QStringList &lines); + + /** + * Emits when received standard error lines from inferior + */ + void inferiorStderrLines(const QStringList &lines); + + void inferiorStopped(const MI::AsyncRecord &r); + + void inferiorRunning(); + + /** + * Emits when received standard output from debugger for user commands + */ + void debuggerUserCommandOutput(const QString &output); + + /** + * Emits when received standard output from debugger for internal commands + */ + void debuggerInternalCommandOutput(const QString &output); + + /** + * Emits when received standard output from inferior's tty + */ + void inferiorTtyStdout(const QByteArray &output); + + /** + * Emits when received standard output from inferior's tty + */ + void inferiorTtyStderr(const QByteArray &output); + + /** + * Emits when the debugger instance state changes + */ + void debuggerStateChanged(DBGStateFlags oldState, DBGStateFlags newState); + + /** + * Emits when there's message needed to be show to user. + */ + void showMessage(const QString& message, int timeout); + + /** + * Emits when the debugger console view need to be raised. + */ + void raiseDebuggerConsoleViews(); + + /** + * Emits when need to reset + */ + void reset(); + +public: + bool debuggerStateIsOn(DBGStateFlags state) const; + DBGStateFlags debuggerState() const; + + bool hasCrashed() const; + +// BEGIN IDebugSession overrides +public: + DebuggerState state() const override; + bool restartAvaliable() const override; + + MIBreakpointController * breakpointController() const override = 0; + +public Q_SLOTS: + void restartDebugger() override; + void stopDebugger() override; + void interruptDebugger() override; + void run() override; + void runToCursor() override; + void jumpToCursor() override; + void stepOver() override; + void stepIntoInstruction() override; + void stepInto() override; + void stepOverInstruction() override; + void stepOut() override; +// END IDebugSession overrides + +public Q_SLOTS: + /** + * Run currently executing program to the given \a url and \a line. + */ + void runUntil(const QUrl& url, int line); + + /** + * Run currently executing program to the given \a address + */ + void runUntil(const QString& address); + + /** + * Move the execution point of the currently executing program to the given \a url and \a line. + */ + void jumpTo(const QUrl& url, int line); + + /** + * Move the execution point of the currently executing program to the given \a address. + *Note: It can be really very dangerous, so use jumpTo instead. + */ + void jumpToMemoryAddress(const QString& address); + + /** + * Start the debugger, and execute the inferior program specified by \a cfg. + */ + bool startDebugging(KDevelop::ILaunchConfiguration *cfg, IExecutePlugin *iexec); + + /** + * Start the debugger, and examine the core file given by \a coreFile. + */ + bool examineCoreFile(const QUrl &debugee, const QUrl &coreFile); + + /** + * Start the debugger, and attach to a currently running process with the given \a pid. + */ + bool attachToProcess(int pid); + + /** Adds a command to the end of queue of commands to be executed + by gdb. The command will be actually sent to gdb only when + replies from all previous commands are received and full processed. + + The literal command sent to gdb is obtained by calling + cmd->cmdToSend. The call is made immediately before sending the + command, so it's possible to use results of prior commands when + computing the exact command to send. + */ + void addCommand(MI::MICommand* cmd); + + /** Same as above, but internally constructs new MI::MICommand + instance from the string. */ + void addCommand(MI::CommandType type, const QString& cmd = QString()); + + void addUserCommand(const QString &cmd); + +protected Q_SLOTS: + virtual void slotDebuggerReady(); + virtual void slotDebuggerExited(bool abnormal, const QString &msg); + virtual void slotInferiorStopped(const MI::AsyncRecord &r); + /** + * Triggered every time program begins/continues it's execution. + */ + virtual void slotInferiorRunning(); + + /** + * Handle MI async notifications. + */ + virtual void processNotification(const MI::AsyncRecord &n); + + /** Default handler for errors. + Tries to guess is the error message is telling that target is + gone, if so, informs the user. + Otherwise, shows a dialog box and reloads view state. */ + virtual void defaultErrorHandler(const MI::ResultRecord &result); + + /** + * Update session state when debugger state changes, and show messages + */ + virtual void handleDebuggerStateChange(DBGStateFlags oldState, DBGStateFlags newState); + + void handleNoInferior(const QString &msg); + void handleInferiorFinished(const QString &msg); + +protected: + void queueCmd(MI::MICommand *cmd); + + /** Try to execute next command in the queue. If GDB is not + busy with previous command, and there's a command in the + queue, sends it. */ + void executeCmd(); + void ensureDebuggerListening(); + void destroyCmds(); + + /** + * Start the debugger instance + */ + bool startDebugger(KDevelop::ILaunchConfiguration *cfg); + + /** + * MIDebugSession takes the ownership of the created instance. + */ + virtual MIDebugger *createDebugger() const = 0; + + /** + * Initialize debugger and set default configurations. + */ + virtual void initializeDebugger() = 0; + + /** + * Further config the debugger and start the inferior program (either local or remote). + */ + virtual bool execInferior(KDevelop::ILaunchConfiguration *cfg, const QString &executable) = 0; + + /** + * Manipulate debugger instance state + */ + void setDebuggerStateOn(DBGStateFlags stateOn); + void setDebuggerStateOff(DBGStateFlags stateOff); + void setDebuggerState(DBGStateFlags newState); + + void debuggerStateChange(DBGStateFlags oldState, DBGStateFlags newState); + + /** + * Manipulate the session state + */ + void setSessionState(DebuggerState state); + + void raiseEvent(event_t e) override; + + /** Called when there are no pending commands and 'm_stateReloadNeeded' + is true. Also can be used to immediately reload program state. + Issues commands to completely reload all program state shown + to the user. + */ + void reloadProgramState(); + + void programNoApp(const QString &msg); + void programFinished(const QString &msg); + + // FIXME: Whether let the debugger source init files when starting, + // only used in unit test currently, potentially could be made a user + // configurable option + void setSourceInitFile(bool enable); + +private Q_SLOTS: + void handleTargetAttach(const MI::ResultRecord& r); + void handleCoreFile(const MI::ResultRecord& r); + // Pops up a dialog box with some hopefully + // detailed information about which state debugger + // is in, which commands were sent and so on. + void explainDebuggerStatus(); + +protected: + KDevelop::ProcessLineMaker *m_procLineMaker; + + std::unique_ptr m_commandQueue; + + // Though the misleading class name, this is the session level state. + // see m_debuggerState for debugger instance state + DebuggerState m_sessionState; + + MIDebugger *m_debugger; + DBGStateFlags m_debuggerState; + + bool m_stateReloadInProgress; + bool m_stateReloadNeeded; + + std::unique_ptr m_tty; + + bool m_hasCrashed; + bool m_sourceInitFile; +}; + +} // end of namespace KDevMI + +#endif // MIDEBUGSESSION_H diff --git a/debuggers/common/midebugsession.cpp b/debuggers/common/midebugsession.cpp new file mode 100644 --- /dev/null +++ b/debuggers/common/midebugsession.cpp @@ -0,0 +1,1268 @@ +/* + * Common code for debugger support + * + * Copyright 1999-2001 John Birch + * Copyright 2001 by Bernd Gehrmann + * Copyright 2006 Vladimir Prus + * Copyright 2007 Hamish Rodda + * Copyright 2009 Niko Sams + * 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 "midebugsession.h" + +#include "debuglog.h" +#include "midebugger.h" +#include "mi/mi.h" +#include "mi/micommand.h" +#include "mi/micommandqueue.h" +#include "stty.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; +using namespace KDevMI; +using namespace KDevMI::MI; + +MIDebugSession::MIDebugSession() + : m_procLineMaker(new ProcessLineMaker(this)) + , m_commandQueue(new CommandQueue) + , m_sessionState(NotStartedState) + , m_debugger(nullptr) + , m_debuggerState(s_dbgNotStarted | s_appNotStarted) + , m_stateReloadInProgress(false) + , m_stateReloadNeeded(false) + , m_tty(nullptr) + , m_hasCrashed(false) + , m_sourceInitFile(true) +{ + // setup signals + connect(m_procLineMaker, &ProcessLineMaker::receivedStdoutLines, + this, &MIDebugSession::inferiorStdoutLines); + connect(m_procLineMaker, &ProcessLineMaker::receivedStderrLines, + this, &MIDebugSession::inferiorStderrLines); + + // forward tty output to process line maker + connect(this, &MIDebugSession::inferiorTtyStdout, + m_procLineMaker, &ProcessLineMaker::slotReceivedStdout); + connect(this, &MIDebugSession::inferiorTtyStderr, + m_procLineMaker, &ProcessLineMaker::slotReceivedStderr); + + // FIXME: see if this still works + //connect(statusBarIndicator, SIGNAL(doubleClicked()), + // controller, SLOT(explainDebuggerStatus())); + + // FIXME: reimplement / re-enable + //connect(this, SIGNAL(addWatchVariable(QString)), controller->variables(), SLOT(slotAddWatchVariable(QString))); + //connect(this, SIGNAL(evaluateExpression(QString)), controller->variables(), SLOT(slotEvaluateExpression(QString))); +} + +MIDebugSession::~MIDebugSession() +{ + qCDebug(DEBUGGERCOMMON) << "Destroying MIDebugSession"; + // Deleting the session involves shutting down gdb nicely. + // When were attached to a process, we must first detach so that the process + // can continue running as it was before being attached. gdb is quite slow to + // detach from a process, so we must process events within here to get a "clean" + // shutdown. + if (!debuggerStateIsOn(s_dbgNotStarted)) { + stopDebugger(); + } +} + +IDebugSession::DebuggerState MIDebugSession::state() const +{ + return m_sessionState; +} + +bool MIDebugSession::restartAvaliable() const +{ + if (debuggerStateIsOn(s_attached) || debuggerStateIsOn(s_core)) { + return false; + } else { + return true; + } +} + +bool MIDebugSession::startDebugger(ILaunchConfiguration *cfg) +{ + qCDebug(DEBUGGERCOMMON) << "Starting new debugger instance"; + if (m_debugger) { + qCWarning(DEBUGGERCOMMON) << "m_debugger object still exists"; + delete m_debugger; + m_debugger = nullptr; + } + m_debugger = createDebugger(); + m_debugger->setParent(this); + + // output signals + connect(m_debugger, &MIDebugger::applicationOutput, + this, [this](const QString &output) { + emit inferiorStdoutLines(output.split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts)); + }); + connect(m_debugger, &MIDebugger::userCommandOutput, this, &MIDebugSession::debuggerUserCommandOutput); + connect(m_debugger, &MIDebugger::internalCommandOutput, this, &MIDebugSession::debuggerInternalCommandOutput); + + // state signals + connect(m_debugger, &MIDebugger::programStopped, this, &MIDebugSession::inferiorStopped); + connect(m_debugger, &MIDebugger::programRunning, this, &MIDebugSession::inferiorRunning); + + // internal handlers + connect(m_debugger, &MIDebugger::ready, this, &MIDebugSession::slotDebuggerReady); + connect(m_debugger, &MIDebugger::exited, this, &MIDebugSession::slotDebuggerExited); + connect(m_debugger, &MIDebugger::programStopped, this, &MIDebugSession::slotInferiorStopped); + connect(m_debugger, &MIDebugger::programRunning, this, &MIDebugSession::slotInferiorRunning); + connect(m_debugger, &MIDebugger::notification, this, &MIDebugSession::processNotification); + + + // start the debugger. Do this after connecting all signals so that initial + // debugger output, and important events like the debugger died are reported. + QStringList extraArguments; + if (!m_sourceInitFile) + extraArguments << "--nx"; + + auto config = cfg ? cfg->config() + // FIXME: this is only used when attachToProcess or examineCoreFile. + // Change to use a global launch configuration when calling + : KConfigGroup(KSharedConfig::openConfig(), "GDB Config"); + + m_debugger->start(config, extraArguments); + + // FIXME: here, we should wait until the debugger is up and waiting for input. + // Then, clear s_dbgNotStarted + // It's better to do this right away so that the state bit is always correct. + setDebuggerStateOff(s_dbgNotStarted); + + // Initialise debugger. At this stage debugger is sitting wondering what to do, + // and to whom. + initializeDebugger(); + + qCDebug(DEBUGGERCOMMON) << "Debugger instance started"; + return true; +} + +bool MIDebugSession::startDebugging(ILaunchConfiguration* cfg, IExecutePlugin* iexec) +{ + qCDebug(DEBUGGERCOMMON) << "Starting new debug session"; + Q_ASSERT(cfg); + Q_ASSERT(iexec); + + // Ensure debugger is started first + if (debuggerStateIsOn(s_appNotStarted)) { + emit showMessage(i18n("Running program"), 1000); + } + + if (debuggerStateIsOn(s_dbgNotStarted)) { + if (!startDebugger(cfg)) + return false; + } + + if (debuggerStateIsOn(s_shuttingDown)) { + qCDebug(DEBUGGERCOMMON) << "Tried to run when debugger shutting down"; + return false; + } + + // Set up the tty for the inferior + bool config_useExternalTerminal = iexec->useTerminal(cfg); + QString config_ternimalName = iexec->terminal(cfg); + if (!config_ternimalName.isEmpty()) { + // the external terminal cmd contains additional arguments, just get the terminal name + config_ternimalName = KShell::splitArgs(config_ternimalName).first(); + } + + m_tty.reset(new STTY(config_useExternalTerminal, config_ternimalName)); + if (!config_useExternalTerminal) { + connect(m_tty.get(), &STTY::OutOutput, this, &MIDebugSession::inferiorTtyStdout); + connect(m_tty.get(), &STTY::ErrOutput, this, &MIDebugSession::inferiorTtyStderr); + } + QString tty(m_tty->getSlave()); + if (tty.isEmpty()) { + KMessageBox::information(qApp->activeWindow(), m_tty->lastError(), i18n("warning")); + + m_tty.reset(nullptr); + return false; + } + queueCmd(new MICommand(InferiorTtySet, tty)); + + // Only dummy err here, actual erros have been checked already in the job and we don't get here if there were any + QString err; + QString executable = iexec->executable(cfg, err).toLocalFile(); + QStringList arguments = iexec->arguments(cfg, err); + // Change the working directory to the correct one + QString dir = iexec->workingDirectory(cfg).toLocalFile(); + if (dir.isEmpty()) { + dir = QFileInfo(executable).absolutePath(); + } + queueCmd(new MICommand(MI::EnvironmentCd, '"' + dir + '"')); + + // Set the environment variables + EnvironmentGroupList l(KSharedConfig::openConfig()); + QString envgrp = iexec->environmentGroup(cfg); + if (envgrp.isEmpty()) { + qCWarning(DEBUGGERCOMMON) << i18n("No environment group specified, looks like a broken " + "configuration, please check run configuration '%1'. " + "Using default environment group.", cfg->name()); + envgrp = l.defaultGroup(); + } + for (const auto &envvar : l.createEnvironment(envgrp, {})) { + queueCmd(new MICommand(MI::GdbSet, "environment " + envvar)); + } + + // Set the run arguments + if (!arguments.isEmpty()) + queueCmd(new MICommand(MI::ExecArguments, KShell::joinArgs(arguments))); + + // Do other debugger specific config options and actually start the inferior program + if (!execInferior(cfg, executable)) { + return false; + } + + QString config_startWith = cfg->config().readEntry(startWithEntry, QStringLiteral("ApplicationOutput")); + if (config_startWith == "GdbConsole") { + emit raiseDebuggerConsoleViews(); + } else if (config_startWith == "FrameStack") { + emit raiseFramestackViews(); + } else { + // ApplicationOutput is raised in DebugJob (by setting job to Verbose/Silent) + } + + return true; +} + +// FIXME: use same configuration process as startDebugging +bool MIDebugSession::attachToProcess(int pid) +{ + qCDebug(DEBUGGERCOMMON) << "Attach to process" << pid; + + if (debuggerStateIsOn(s_dbgNotStarted)) { + // FIXME: use global launch configuration rather than nullptr + if (!startDebugger(nullptr)) { + return false; + } + } + + setDebuggerStateOn(s_attached); + + //set current state to running, after attaching we will get *stopped response + setDebuggerStateOn(s_appRunning); + + // Currently, we always start debugger with a name of binary, + // we might be connecting to a different binary completely, + // so cancel all symbol tables gdb has. + // We can't omit application name from gdb invocation + // because for libtool binaries, we have no way to guess + // real binary name. + queueCmd(new MICommand(MI::FileExecAndSymbols)); + + queueCmd(new MICommand(MI::TargetAttach, QString::number(pid), + this, &MIDebugSession::handleTargetAttach, + CmdHandlesError)); + + queueCmd(new SentinelCommand(breakpointController(), + &MIBreakpointController::initSendBreakpoints)); + + raiseEvent(connected_to_program); + + emit raiseFramestackViews(); + + return true; +} + +void MIDebugSession::handleTargetAttach(const MI::ResultRecord& r) +{ + if (r.reason == "error") { + KMessageBox::error( + qApp->activeWindow(), + i18n("Could not attach debugger:
")+ + r["msg"].literal(), + i18n("Startup error")); + stopDebugger(); + } +} + +bool MIDebugSession::examineCoreFile(const QUrl &debugee, const QUrl &coreFile) +{ + if (debuggerStateIsOn(s_dbgNotStarted)) { + // FIXME: use global launch configuration rather than nullptr + if (!startDebugger(nullptr)) { + return false; + } + } + + // FIXME: support non-local URLs + queueCmd(new MICommand(MI::FileExecAndSymbols, debugee.toLocalFile())); + queueCmd(new MICommand(MI::NonMI, "core " + coreFile.toLocalFile(), + this, &MIDebugSession::handleCoreFile, CmdHandlesError)); + + raiseEvent(connected_to_program); + raiseEvent(program_state_changed); + + return true; +} + +void MIDebugSession::handleCoreFile(const MI::ResultRecord& r) +{ + if (r.reason != "error") { + setDebuggerStateOn(s_programExited|s_core); + } else { + KMessageBox::information( + qApp->activeWindow(), + i18n("Failed to load core file" + "

Debugger reported the following error:" + "

%1", r["msg"].literal()), + i18n("Debugger error")); + + // FIXME: How should we proceed at this point? Stop the debugger? + } +} + +#define ENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) +void MIDebugSession::setSessionState(DebuggerState state) +{ + qCDebug(DEBUGGERCOMMON) << "Session state changed to" + << ENUM_NAME(IDebugSession, DebuggerState, state) + << "(" << state << ")"; + if (state != m_sessionState) { + m_sessionState = state; + emit stateChanged(state); + } +} + +bool MIDebugSession::debuggerStateIsOn(DBGStateFlags state) const +{ + return m_debuggerState & state; +} + +DBGStateFlags MIDebugSession::debuggerState() const +{ + return m_debuggerState; +} + +void MIDebugSession::setDebuggerStateOn(DBGStateFlags stateOn) +{ + DBGStateFlags oldState = m_debuggerState; + + debuggerStateChange(m_debuggerState, m_debuggerState | stateOn); + m_debuggerState |= stateOn; + + handleDebuggerStateChange(oldState, m_debuggerState); +} + +void MIDebugSession::setDebuggerStateOff(DBGStateFlags stateOff) +{ + DBGStateFlags oldState = m_debuggerState; + + debuggerStateChange(m_debuggerState, m_debuggerState & ~stateOff); + m_debuggerState &= ~stateOff; + + handleDebuggerStateChange(oldState, m_debuggerState); +} + +void MIDebugSession::setDebuggerState(DBGStateFlags newState) +{ + DBGStateFlags oldState = m_debuggerState; + + debuggerStateChange(m_debuggerState, newState); + m_debuggerState = newState; + + handleDebuggerStateChange(oldState, m_debuggerState); +} + +void MIDebugSession::debuggerStateChange(DBGStateFlags oldState, DBGStateFlags newState) +{ + int delta = oldState ^ newState; + if (delta) + { + QString out; +#define STATE_CHECK(name) \ + do { \ + if (delta & name) { \ + out += ((newState & name) ? " +" : " -"); \ + out += #name; \ + delta &= ~name; \ + } \ + } while (0) + STATE_CHECK(s_dbgNotStarted); + STATE_CHECK(s_appNotStarted); + STATE_CHECK(s_programExited); + STATE_CHECK(s_attached); + STATE_CHECK(s_core); + STATE_CHECK(s_shuttingDown); + STATE_CHECK(s_dbgBusy); + STATE_CHECK(s_appRunning); + STATE_CHECK(s_dbgNotListening); + STATE_CHECK(s_automaticContinue); +#undef STATE_CHECK + + for (unsigned int i = 0; delta != 0 && i < 32; ++i) { + if (delta & (1 << i)) { + delta &= ~(1 << i); + out += ((1 << i) & newState) ? " +" : " -"; + out += QString::number(i); + } + } + qCDebug(DEBUGGERCOMMON) << "Debugger state change:" << out; + } +} + +void MIDebugSession::handleDebuggerStateChange(DBGStateFlags oldState, DBGStateFlags newState) +{ + QString message; + + DebuggerState oldSessionState = state(); + DebuggerState newSessionState = oldSessionState; + DBGStateFlags changedState = oldState ^ newState; + + if (newState & s_dbgNotStarted) { + if (changedState & s_dbgNotStarted) { + message = i18n("Debugger stopped"); + emit finished(); + } + if (oldSessionState != NotStartedState) { + newSessionState = EndedState; + } + } else { + if (newState & s_appNotStarted) { + if (oldSessionState == NotStartedState || oldSessionState == StartingState) { + newSessionState = StartingState; + } else { + newSessionState = StoppedState; + } + } else if (newState & s_programExited) { + if (changedState & s_programExited) { + message = i18n("Process exited"); + } + newSessionState = StoppedState; + } else if (newState & s_appRunning) { + if (changedState & s_appRunning) { + message = i18n("Application is running"); + } + newSessionState = ActiveState; + } else { + if (changedState & s_appRunning) { + message = i18n("Application is paused"); + } + newSessionState = PausedState; + } + } + + // And now? :-) + qCDebug(DEBUGGERCOMMON) << "Debugger state changed to: " << newState << message; + + if (!message.isEmpty()) + emit showMessage(message, 3000); + + emit debuggerStateChanged(oldState, newState); + + // must be last, since it can lead to deletion of the DebugSession + if (newSessionState != oldSessionState) { + setSessionState(newSessionState); + } +} + +void MIDebugSession::restartDebugger() +{ + // We implement restart as kill + slotRun, as opposed as plain "run" + // command because kill + slotRun allows any special logic in slotRun + // to apply for restart. + // + // That includes: + // - checking for out-of-date project + // - special setup for remote debugging. + // + // Had we used plain 'run' command, restart for remote debugging simply + // would not work. + if (!debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) { + // FIXME: s_dbgBusy or m_debugger->isReady()? + if (debuggerStateIsOn(s_dbgBusy)) { + interruptDebugger(); + } + // The -exec-abort is not implemented in gdb + // queueCmd(new MICommand(MI::ExecAbort)); + queueCmd(new MICommand(MI::NonMI, "kill")); + } + run(); +} + +void MIDebugSession::stopDebugger() +{ + m_commandQueue->clear(); + + qCDebug(DEBUGGERCOMMON) << "try stopping debugger"; + if (debuggerStateIsOn(s_shuttingDown) || !m_debugger) + return; + + setDebuggerStateOn(s_shuttingDown); + qCDebug(DEBUGGERCOMMON) << "stopping debugger"; + + // Get debugger's attention if it's busy. We need debugger to be at the + // command line so we can stop it. + if (!m_debugger->isReady()) { + qCDebug(DEBUGGERCOMMON) << "debugger busy on shutdown - interruping"; + interruptDebugger(); + } + + // If the app is attached then we release it here. This doesn't stop + // the app running. + if (debuggerStateIsOn(s_attached)) { + queueCmd(new MICommand(MI::TargetDetach)); + emit debuggerUserCommandOutput("(gdb) detach\n"); + } + + // Now try to stop debugger running. + queueCmd(new MICommand(MI::GdbExit)); + emit debuggerUserCommandOutput("(gdb) quit"); + + // We cannot wait forever, kill gdb after 5 seconds if it's not yet quit + QPointer guarded_this(this); + QTimer::singleShot(5000, [guarded_this](){ + if (guarded_this) { + if (!guarded_this->debuggerStateIsOn(s_programExited) + && guarded_this->debuggerStateIsOn(s_shuttingDown)) { + qCDebug(DEBUGGERCOMMON) << "debugger not shutdown - killing"; + guarded_this->m_debugger->kill(); + guarded_this->setDebuggerState(s_dbgNotStarted | s_appNotStarted); + guarded_this->raiseEvent(debugger_exited); + } + } + }); + + emit reset(); +} + +void MIDebugSession::interruptDebugger() +{ + Q_ASSERT(m_debugger); + + // Explicitly send the interrupt in case something went wrong with the usual + // ensureGdbListening logic. + m_debugger->interrupt(); + queueCmd(new MICommand(MI::ExecInterrupt, QString(), CmdInterrupt)); +} + +void MIDebugSession::run() +{ + if (debuggerStateIsOn(s_appNotStarted|s_dbgNotStarted|s_shuttingDown)) + return; + + queueCmd(new MICommand(MI::ExecContinue, QString(), CmdMaybeStartsRunning)); +} + +void MIDebugSession::runToCursor() +{ + if (IDocument* doc = ICore::self()->documentController()->activeDocument()) { + KTextEditor::Cursor cursor = doc->cursorPosition(); + if (cursor.isValid()) + runUntil(doc->url(), cursor.line() + 1); + } +} + +void MIDebugSession::jumpToCursor() +{ + if (IDocument* doc = ICore::self()->documentController()->activeDocument()) { + KTextEditor::Cursor cursor = doc->cursorPosition(); + if (cursor.isValid()) + jumpTo(doc->url(), cursor.line() + 1); + } +} + +void MIDebugSession::stepOver() +{ + if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) + return; + + queueCmd(new MICommand(MI::ExecNext, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); +} + +void MIDebugSession::stepIntoInstruction() +{ + if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) + return; + + queueCmd(new MICommand(MI::ExecStepInstruction, QString(), + CmdMaybeStartsRunning | CmdTemporaryRun)); +} + +void MIDebugSession::stepInto() +{ + if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) + return; + + queueCmd(new MICommand(MI::ExecStep, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); +} + +void MIDebugSession::stepOverInstruction() +{ + if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) + return; + + queueCmd(new MICommand(MI::ExecNextInstruction, QString(), + CmdMaybeStartsRunning | CmdTemporaryRun)); +} + +void MIDebugSession::stepOut() +{ + if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) + return; + + queueCmd(new MICommand(MI::ExecFinish, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); +} + +void MIDebugSession::runUntil(const QUrl& url, int line) +{ + if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) + return; + + if (!url.isValid()) { + queueCmd(new MICommand(MI::ExecUntil, QString::number(line), + CmdMaybeStartsRunning | CmdTemporaryRun)); + } else { + queueCmd(new MICommand(MI::ExecUntil, + QString("%1:%2").arg(url.toLocalFile()).arg(line), + CmdMaybeStartsRunning | CmdTemporaryRun)); + } +} + +void MIDebugSession::runUntil(const QString& address) +{ + if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) + return; + + if (!address.isEmpty()) { + queueCmd(new MICommand(MI::ExecUntil, QString("*%1").arg(address), + CmdMaybeStartsRunning | CmdTemporaryRun)); + } +} + +void MIDebugSession::jumpTo(const QUrl& url, int line) +{ + if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) + return; + + if (url.isValid()) { + queueCmd(new MICommand(MI::NonMI, + QString("tbreak %1:%2").arg(url.toLocalFile()).arg(line))); + queueCmd(new MICommand(MI::NonMI, + QString("jump %1:%2").arg(url.toLocalFile()).arg(line))); + } +} + +void MIDebugSession::jumpToMemoryAddress(const QString& address) +{ + if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) + return; + + if (!address.isEmpty()) { + queueCmd(new MICommand(MI::NonMI, QString("tbreak *%1").arg(address))); + queueCmd(new MICommand(MI::NonMI, QString("jump *%1").arg(address))); + } +} + +void MIDebugSession::addUserCommand(const QString& cmd) +{ + queueCmd(new UserCommand(MI::NonMI, cmd)); + + // User command can theoreticall modify absolutely everything, + // so need to force a reload. + + // We can do it right now, and don't wait for user command to finish + // since commands used to reload all view will be executed after + // user command anyway. + if (!debuggerStateIsOn(s_appNotStarted) && !debuggerStateIsOn(s_programExited)) + raiseEvent(program_state_changed); +} + +void MIDebugSession::addCommand(MICommand* cmd) +{ + queueCmd(cmd); +} + +void MIDebugSession::addCommand(MI::CommandType type, const QString& str) +{ + queueCmd(new MICommand(type, str)); +} + +// Fairly obvious that we'll add whatever command you give me to a queue +// Not quite so obvious though is that if we are going to run again. then any +// information requests become redundent and must be removed. +// We also try and run whatever command happens to be at the head of +// the queue. +void MIDebugSession::queueCmd(MICommand *cmd) +{ + if (debuggerStateIsOn(s_dbgNotStarted)) { + KMessageBox::information( + qApp->activeWindow(), + i18n("Gdb command sent when debugger is not running
" + "The command was:
%1", cmd->initialString()), + i18n("Internal error")); + return; + } + + if (m_stateReloadInProgress) + cmd->setStateReloading(true); + + m_commandQueue->enqueue(cmd); + + qCDebug(DEBUGGERCOMMON) << "QUEUE: " << cmd->initialString() + << (m_stateReloadInProgress ? "(state reloading)" : ""); + + bool varCommandWithContext= (cmd->type() >= MI::VarAssign + && cmd->type() <= MI::VarUpdate + && cmd->type() != MI::VarDelete); + + bool stackCommandWithContext = (cmd->type() >= MI::StackInfoDepth + && cmd->type() <= MI::StackListLocals); + + if (varCommandWithContext || stackCommandWithContext) { + if (cmd->thread() == -1) + qCDebug(DEBUGGERCOMMON) << "\t--thread will be added on execution"; + + if (cmd->frame() == -1) + qCDebug(DEBUGGERCOMMON) << "\t--frame will be added on execution"; + } + + setDebuggerStateOn(s_dbgBusy); + raiseEvent(debugger_busy); + + executeCmd(); +} + +void MIDebugSession::executeCmd() +{ + Q_ASSERT(m_debugger); + + if (debuggerStateIsOn(s_dbgNotListening) && m_commandQueue->haveImmediateCommand()) { + // We may have to call this even while a command is currently executing, because + // debugger can get into a state where a command such as ExecRun does not send a response + // while the inferior is running. + ensureDebuggerListening(); + } + + if (!m_debugger->isReady()) + return; + + MICommand* currentCmd = m_commandQueue->nextCommand(); + if (!currentCmd) + return; + + if (currentCmd->flags() & (CmdMaybeStartsRunning | CmdInterrupt)) { + setDebuggerStateOff(s_automaticContinue); + } + + if (currentCmd->flags() & CmdMaybeStartsRunning) { + // GDB can be in a state where it is listening for commands while the program is running. + // However, when we send a command such as ExecContinue in this state, GDB may return to + // the non-listening state without acknowledging that the ExecContinue command has even + // finished, let alone sending a new notification about the program's running state. + // So let's be extra cautious about ensuring that we will wake GDB up again if required. + setDebuggerStateOn(s_dbgNotListening); + } + + bool varCommandWithContext= (currentCmd->type() >= MI::VarAssign + && currentCmd->type() <= MI::VarUpdate + && currentCmd->type() != MI::VarDelete); + + bool stackCommandWithContext = (currentCmd->type() >= MI::StackInfoDepth + && currentCmd->type() <= MI::StackListLocals); + + if (varCommandWithContext || stackCommandWithContext) { + // Most var commands should be executed in the context + // of the selected thread and frame. + if (currentCmd->thread() == -1) + currentCmd->setThread(frameStackModel()->currentThread()); + + if (currentCmd->frame() == -1) + currentCmd->setFrame(frameStackModel()->currentFrame()); + } + + QString commandText = currentCmd->cmdToSend(); + bool bad_command = false; + QString message; + + int length = commandText.length(); + // No i18n for message since it's mainly for debugging. + if (length == 0) { + // The command might decide it's no longer necessary to send + // it. + if (SentinelCommand* sc = dynamic_cast(currentCmd)) + { + qCDebug(DEBUGGERCOMMON) << "SEND: sentinel command, not sending"; + sc->invokeHandler(); + } + else + { + qCDebug(DEBUGGERCOMMON) << "SEND: command " << currentCmd->initialString() + << "changed its mind, not sending"; + } + + delete currentCmd; + executeCmd(); + return; + } else { + if (commandText[length-1] != '\n') { + bad_command = true; + message = "Debugger command does not end with newline"; + } + } + + if (bad_command) { + KMessageBox::information(qApp->activeWindow(), + i18n("Invalid debugger command
%1", message), + i18n("Invalid debugger command")); + executeCmd(); + return; + } + + m_debugger->execute(currentCmd); +} + +void MIDebugSession::ensureDebuggerListening() +{ + Q_ASSERT(m_debugger); + + // Note: we don't use interruptDebugger() here since + // we don't want to queue more commands before queuing a command + m_debugger->interrupt(); + + setDebuggerStateOn(s_interruptSent); + if (debuggerStateIsOn(s_appRunning)) + setDebuggerStateOn(s_automaticContinue); + setDebuggerStateOff(s_dbgNotListening); +} + +void MIDebugSession::destroyCmds() +{ + m_commandQueue->clear(); +} + +// FIXME: I don't fully remember what is the business with +// m_stateReloadInProgress and whether we can lift it to the +// generic level. +void MIDebugSession::raiseEvent(event_t e) +{ + if (e == program_exited || e == debugger_exited) { + m_stateReloadInProgress = false; + } + + if (e == program_state_changed) { + m_stateReloadInProgress = true; + qCDebug(DEBUGGERCOMMON) << "State reload in progress\n"; + } + + IDebugSession::raiseEvent(e); + + if (e == program_state_changed) { + m_stateReloadInProgress = false; + } +} + +bool KDevMI::MIDebugSession::hasCrashed() const +{ + return m_hasCrashed; +} + +void MIDebugSession::slotDebuggerReady() +{ + Q_ASSERT(m_debugger); + + m_stateReloadInProgress = false; + + executeCmd(); + if (m_debugger->isReady()) { + /* There is nothing in the command queue and no command is currently executing. */ + if (debuggerStateIsOn(s_automaticContinue)) { + if (!debuggerStateIsOn(s_appRunning)) { + qCDebug(DEBUGGERCOMMON) << "Posting automatic continue"; + queueCmd(new MICommand(MI::ExecContinue, QString(), CmdMaybeStartsRunning)); + } + setDebuggerStateOff(s_automaticContinue); + return; + } + + if (m_stateReloadNeeded && !debuggerStateIsOn(s_appRunning)) { + qCDebug(DEBUGGERCOMMON) << "Finishing program stop"; + // Set to false right now, so that if 'actOnProgramPauseMI_part2' + // sends some commands, we won't call it again when handling replies + // from that commands. + m_stateReloadNeeded = false; + reloadProgramState(); + } + + qCDebug(DEBUGGERCOMMON) << "No more commands"; + setDebuggerStateOff(s_dbgBusy); + raiseEvent(debugger_ready); + } +} + +void MIDebugSession::slotDebuggerExited(bool abnormal, const QString &msg) +{ + /* Technically speaking, GDB is likely not to kill the application, and + we should have some backup mechanism to make sure the application is + killed by KDevelop. But even if application stays around, we no longer + can control it in any way, so mark it as exited. */ + setDebuggerStateOn(s_appNotStarted); + setDebuggerStateOn(s_dbgNotStarted); + setDebuggerStateOn(s_programExited); + setDebuggerStateOff(s_shuttingDown); + + if (!msg.isEmpty()) + emit showMessage(msg, 3000); + + if (abnormal) { + /* The error is reported to user in MIDebugger now. + KMessageBox::information( + KDevelop::ICore::self()->uiController()->activeMainWindow(), + i18n("Debugger exited abnormally" + "

This is likely a bug in GDB. " + "Examine the gdb output window and then stop the debugger"), + i18n("Debugger exited abnormally")); + */ + // FIXME: not sure if the following still applies. + // Note: we don't stop the debugger here, becuse that will hide gdb + // window and prevent the user from finding the exact reason of the + // problem. + } + + /* FIXME: raiseEvent is handled across multiple places where we explicitly + * stop/kill the debugger, a better way is to let the debugger itself report + * its exited event. + */ + // raiseEvent(debugger_exited); +} + +void MIDebugSession::slotInferiorStopped(const MI::AsyncRecord& r) +{ + /* By default, reload all state on program stop. */ + m_stateReloadNeeded = true; + setDebuggerStateOff(s_appRunning); + setDebuggerStateOff(s_dbgNotListening); + + QString reason; + if (r.hasField("reason")) reason = r["reason"].literal(); + + if (reason == "exited-normally" || reason == "exited") { + if (r.hasField("exit-code")) { + programNoApp(i18n("Exited with return code: %1", r["exit-code"].literal())); + } else { + programNoApp(i18n("Exited normally")); + } + m_stateReloadNeeded = false; + return; + } + + if (reason == "exited-signalled") { + programNoApp(i18n("Exited on signal %1", r["signal-name"].literal())); + m_stateReloadNeeded = false; + return; + } + + if (reason == "watchpoint-scope") { + QString number = r["wpnum"].literal(); + + // FIXME: shuld remove this watchpoint + // But first, we should consider if removing all + // watchpoinst on program exit is the right thing to + // do. + + queueCmd(new MICommand(MI::ExecContinue, QString(), CmdMaybeStartsRunning)); + + m_stateReloadNeeded = false; + return; + } + + bool wasInterrupt = false; + + if (reason == "signal-received") { + QString name = r["signal-name"].literal(); + QString user_name = r["signal-meaning"].literal(); + + // SIGINT is a "break into running program". + // We do this when the user set/mod/clears a breakpoint but the + // application is running. + // And the user does this to stop the program also. + if (name == "SIGINT" && debuggerStateIsOn(s_interruptSent)) { + wasInterrupt = true; + } else { + // Whenever we have a signal raised then tell the user, but don't + // end the program as we want to allow the user to look at why the + // program has a signal that's caused the prog to stop. + // Continuing from SIG FPE/SEGV will cause a "Cannot ..." and + // that'll end the program. + programFinished(i18n("Program received signal %1 (%2)", name, user_name)); + + m_hasCrashed = true; + } + } + + if (!reason.contains("exited")) { + // FIXME: we should immediately update the current thread and + // frame in the framestackmodel, so that any user actions + // are in that thread. However, the way current framestack model + // is implemented, we can't change thread id until we refresh + // the entire list of threads -- otherwise we might set a thread + // id that is not already in the list, and it will be upset. + + //Indicates if program state should be reloaded immediately. + bool updateState = false; + + if (r.hasField("frame")) { + const MI::Value& frame = r["frame"]; + QString file, line, addr; + + if (frame.hasField("fullname")) file = frame["fullname"].literal();; + if (frame.hasField("line")) line = frame["line"].literal(); + if (frame.hasField("addr")) addr = frame["addr"].literal(); + + // gdb counts lines from 1 and we don't + setCurrentPosition(QUrl::fromLocalFile(file), line.toInt() - 1, addr); + + updateState = true; + } + + if (updateState) { + reloadProgramState(); + } + } + + setDebuggerStateOff(s_interruptSent); + if (!wasInterrupt) + setDebuggerStateOff(s_automaticContinue); +} + +void MIDebugSession::slotInferiorRunning() +{ + setDebuggerStateOn(s_appRunning); + raiseEvent(program_running); + + if (m_commandQueue->haveImmediateCommand() || + (m_debugger->currentCommand() && (m_debugger->currentCommand()->flags() & (CmdImmediately | CmdInterrupt)))) { + ensureDebuggerListening(); + } else { + setDebuggerStateOn(s_dbgNotListening); + } +} + +void MIDebugSession::processNotification(const MI::AsyncRecord & async) +{ + if (async.reason == "thread-group-started") { + setDebuggerStateOff(s_appNotStarted | s_programExited); + } else if (async.reason == "thread-group-exited") { + setDebuggerStateOn(s_programExited); + } else if (async.reason == "library-loaded") { + // do nothing + } else if (async.reason == "breakpoint-created") { + breakpointController()->notifyBreakpointCreated(async); + } else if (async.reason == "breakpoint-modified") { + breakpointController()->notifyBreakpointModified(async); + } else if (async.reason == "breakpoint-deleted") { + breakpointController()->notifyBreakpointDeleted(async); + } else { + qCDebug(DEBUGGERCOMMON) << "Unhandled notification: " << async.reason; + } +} + +void MIDebugSession::reloadProgramState() +{ + raiseEvent(program_state_changed); + m_stateReloadNeeded = false; +} + +// There is no app anymore. This can be caused by program exiting +// an invalid program specified or ... +// gdb is still running though, but only the run command (may) make sense +// all other commands are disabled. +void MIDebugSession::programNoApp(const QString& msg) +{ + qCDebug(DEBUGGERCOMMON) << msg; + + setDebuggerState(s_appNotStarted | s_programExited | (m_debuggerState & s_shuttingDown)); + + destroyCmds(); + + // The application has existed, but it's possible that + // some of application output is still in the pipe. We use + // different pipes to communicate with gdb and to get application + // output, so "exited" message from gdb might have arrived before + // last application output. Get this last bit. + + // Note: this method can be called when we open an invalid + // core file. In that case, tty_ won't be set. + if (m_tty){ + m_tty->readRemaining(); + // Tty is no longer usable, delete it. Without this, QSocketNotifier + // will continiously bomd STTY with signals, so we need to either disable + // QSocketNotifier, or delete STTY. The latter is simpler, since we can't + // reuse it for future debug sessions anyway. + m_tty.reset(nullptr); + } + + stopDebugger(); + + raiseEvent(program_exited); + raiseEvent(debugger_exited); + + emit showMessage(msg, 0); + + programFinished(msg); +} + +void MIDebugSession::programFinished(const QString& msg) +{ + QString m = QString("*** %0 ***").arg(msg.trimmed()); + emit inferiorStderrLines(QStringList(m)); + + /* Also show message in gdb window, so that users who + prefer to look at gdb window know what's up. */ + emit debuggerUserCommandOutput(m); +} + +void MIDebugSession::explainDebuggerStatus() +{ + MICommand* currentCmd_ = m_debugger->currentCommand(); + QString information = + i18np("1 command in queue\n", "%1 commands in queue\n", m_commandQueue->count()) + + i18ncp("Only the 0 and 1 cases need to be translated", "1 command being processed by gdb\n", "%1 commands being processed by gdb\n", (currentCmd_ ? 1 : 0)) + + i18n("Debugger state: %1\n", m_debuggerState); + + if (currentCmd_) { + QString extra = i18n("Current command class: '%1'\n" + "Current command text: '%2'\n" + "Current command original text: '%3'\n", + typeid(*currentCmd_).name(), + currentCmd_->cmdToSend(), + currentCmd_->initialString()); + + information += extra; + } + + KMessageBox::information(qApp->activeWindow(), information, + i18n("Debugger status")); +} + +// There is no app anymore. This can be caused by program exiting +// an invalid program specified or ... +// gdb is still running though, but only the run command (may) make sense +// all other commands are disabled. +void MIDebugSession::handleNoInferior(const QString& msg) +{ + qCDebug(DEBUGGERCOMMON) << msg; + + setDebuggerState(s_appNotStarted | s_programExited | (debuggerState() & s_shuttingDown)); + + destroyCmds(); + + // The application has existed, but it's possible that + // some of application output is still in the pipe. We use + // different pipes to communicate with gdb and to get application + // output, so "exited" message from gdb might have arrived before + // last application output. Get this last bit. + + // Note: this method can be called when we open an invalid + // core file. In that case, tty_ won't be set. + if (m_tty){ + m_tty->readRemaining(); + // Tty is no longer usable, delete it. Without this, QSocketNotifier + // will continiously bomd STTY with signals, so we need to either disable + // QSocketNotifier, or delete STTY. The latter is simpler, since we can't + // reuse it for future debug sessions anyway. + m_tty.reset(0); + } + + stopDebugger(); + + raiseEvent(program_exited); + raiseEvent(debugger_exited); + + emit showMessage(msg, 0); + + handleInferiorFinished(msg); +} + +void MIDebugSession::handleInferiorFinished(const QString& msg) +{ + QString m = QStringLiteral("*** %0 ***").arg(msg.trimmed()); + emit inferiorStderrLines(QStringList(m)); + + /* Also show message in gdb window, so that users who + prefer to look at gdb window know what's up. */ + emit debuggerUserCommandOutput(m); +} + +// FIXME: connect to debugger's slot. +void MIDebugSession::defaultErrorHandler(const MI::ResultRecord& result) +{ + QString msg = result["msg"].literal(); + + if (msg.contains("No such process")) + { + setDebuggerState(s_appNotStarted|s_programExited); + raiseEvent(program_exited); + return; + } + + KMessageBox::information( + qApp->activeWindow(), + i18n("Debugger error" + "

Debugger reported the following error:" + "

%1", result["msg"].literal()), + i18n("Debugger error")); + + // Error most likely means that some change made in GUI + // was not communicated to the gdb, so GUI is now not + // in sync with gdb. Resync it. + // + // Another approach is to make each widget reload it content + // on errors from commands that it sent, but that's too complex. + // Errors are supposed to happen rarely, so full reload on error + // is not a big deal. Well, maybe except for memory view, but + // it's no auto-reloaded anyway. + // + // Also, don't reload state on errors appeared during state + // reloading! + if (!m_debugger->currentCommand()->stateReloading()) + raiseEvent(program_state_changed); +} + +void MIDebugSession::setSourceInitFile(bool enable) +{ + m_sourceInitFile = enable; +} diff --git a/debuggers/gdb/selectcoredialog.h b/debuggers/common/miframestackmodel.h copy from debuggers/gdb/selectcoredialog.h copy to debuggers/common/miframestackmodel.h --- a/debuggers/gdb/selectcoredialog.h +++ b/debuggers/common/miframestackmodel.h @@ -1,7 +1,7 @@ /* - * GDB Debugger Support - * - * Copyright 2009 Niko Sams + * Implementation of thread and frame model that are common to debuggers using MI. + * + * Copyright 2009 Vladimir Prus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as @@ -19,27 +19,33 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef SELECTCOREDIALOG_H -#define SELECTCOREDIALOG_H +#ifndef KDEVELOP_MI_FRAMESTACKMODEL_H +#define KDEVELOP_MI_FRAMESTACKMODEL_H -#include -#include +#include -#include "ui_selectcoredialog.h" +namespace KDevMI { -namespace GDBDebugger { +namespace MI { +struct ResultRecord; +} -class SelectCoreDialog : public QDialog +class MIDebugSession; +class MIFrameStackModel : public KDevelop::FrameStackModel { public: - SelectCoreDialog(QWidget *parent = 0); - QUrl binary() const; - QUrl core() const; + MIFrameStackModel( MIDebugSession* session); + + MIDebugSession* session(); + +protected: // FrameStackModel overrides + void fetchThreads() override; + void fetchFrames(int threadNumber, int from, int to) override; private: - Ui::SelectCoreDialog m_ui; + void handleThreadInfo(const MI::ResultRecord& r); }; -} +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/gdbframestackmodel.cpp b/debuggers/common/miframestackmodel.cpp copy from debuggers/gdb/gdbframestackmodel.cpp copy to debuggers/common/miframestackmodel.cpp --- a/debuggers/gdb/gdbframestackmodel.cpp +++ b/debuggers/common/miframestackmodel.cpp @@ -1,6 +1,6 @@ /* - * GDB-specific implementation of thread and frame model. - * + * Implementation of thread and frame model that are common to debuggers using MI. + * * Copyright 2009 Vladimir Prus * * This program is free software; you can redistribute it and/or modify @@ -19,22 +19,26 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "gdbframestackmodel.h" -#include "gdbcommand.h" +#include "miframestackmodel.h" + +#include "midebugsession.h" +#include "mi/micommand.h" #include using namespace KDevelop; +using namespace KDevMI; +using namespace KDevMI::MI; -QString getFunctionOrAddress(const GDBMI::Value &frame) +QString getFunctionOrAddress(const Value &frame) { if (frame.hasField("func")) return frame["func"].literal(); else return frame["addr"].literal(); } -QPair getSource(const GDBMI::Value &frame) +QPair getSource(const Value &frame) { QPair ret(QString(), -1); if (frame.hasField("fullname")) @@ -47,29 +51,42 @@ return ret; } -void GdbFrameStackModel::fetchThreads() +MIFrameStackModel::MIFrameStackModel(MIDebugSession * session) + : FrameStackModel(session) +{ +} + +MIDebugSession * MIFrameStackModel::session() +{ + return static_cast(FrameStackModel::session()); +} + +void MIFrameStackModel::fetchThreads() { + // TODO: preliminary test shows there might be a bug in lldb-mi + // that's causing std::logic_error when executing -thread-info with + // more than one threads. Find a workaround for this (and report bug + // if it truely is). session()->addCommand( - new GDBCommand(GDBMI::ThreadInfo, "", - this, - &GdbFrameStackModel::handleThreadInfo)); + new MICommand(ThreadInfo, "", + this, &MIFrameStackModel::handleThreadInfo)); } -void GdbFrameStackModel::handleThreadInfo(const GDBMI::ResultRecord& r) +void MIFrameStackModel::handleThreadInfo(const ResultRecord& r) { - const GDBMI::Value& threads = r["threads"]; + const Value& threads = r["threads"]; // Traverse GDB threads in backward order -- since GDB // reports them in backward order. We want UI to // show thread IDs in the natural order. - // FIXME: make the code independent of whatever craziness - // gdb might have tomorrow. + // FIXME: at least GDB 7.11 is reporting in the right order, + // consider sort the list afterwards. QList threadsList; int gidx = threads.size()-1; for (; gidx >= 0; --gidx) { KDevelop::FrameStackModel::ThreadItem i; - const GDBMI::Value & threadMI = threads[gidx]; + const Value & threadMI = threads[gidx]; i.nr = threadMI["id"].toInt(); if (threadMI["state"].literal() == "stopped") { i.name = getFunctionOrAddress(threads[gidx]["frame"]); @@ -90,18 +107,18 @@ } } -struct FrameListHandler : public GDBCommandHandler +struct FrameListHandler : public MICommandHandler { - FrameListHandler(GdbFrameStackModel* model, int thread, int to) + FrameListHandler(MIFrameStackModel* model, int thread, int to) : model(model), m_thread(thread) , m_to(to) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { - const GDBMI::Value& stack = r["stack"]; + const Value& stack = r["stack"]; int first = stack[0]["level"].toInt(); QList frames; for (int i = 0; i< stack.size(); ++i) { - const GDBMI::Value& frame = stack[i]; + const Value& frame = stack[i]; KDevelop::FrameStackModel::FrameItem f; f.nr = frame["level"].toInt(); f.name = getFunctionOrAddress(frame); @@ -125,17 +142,17 @@ model->setHasMoreFrames(m_thread, hasMore); } private: - GdbFrameStackModel* model; + MIFrameStackModel* model; int m_thread; int m_to; }; -void GdbFrameStackModel::fetchFrames(int threadNumber, int from, int to) +void MIFrameStackModel::fetchFrames(int threadNumber, int from, int to) { //to+1 so we know if there are more QString arg = QString("%1 %2").arg(from).arg(to+1); - GDBCommand *c = new GDBCommand(GDBMI::StackListFrames, arg, - new FrameListHandler(this, threadNumber, to)); + MICommand *c = new MICommand(StackListFrames, arg, + new FrameListHandler(this, threadNumber, to)); c->setThread(threadNumber); session()->addCommand(c); } diff --git a/debuggers/common/mivariable.h b/debuggers/common/mivariable.h new file mode 100644 --- /dev/null +++ b/debuggers/common/mivariable.h @@ -0,0 +1,79 @@ +/* + * MI based debugger specific Variable + * + * Copyright 2009 Vladimir Prus + * + * 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 MIVARIABLE_H +#define MIVARIABLE_H + +#include "mi/mi.h" + +#include + +#include + + +class CreateVarobjHandler; +class FetchMoreChildrenHandler; +namespace KDevMI { + +class MIVariable : public KDevelop::Variable +{ +public: + MIVariable(KDevelop::TreeModel* model, KDevelop::TreeItem* parent, + const QString& expression, const QString& display = ""); + + ~MIVariable(); + + /* FIXME: should eventually remove, so that existance of + varobjs is fully encapsulalated inside GdbVariable. */ + const QString& varobj() const; + void handleUpdate(const MI::Value& var); + + static MIVariable *findByVarobjName(const QString& varobjName); + + /* Called when debugger dies. Clears the association between varobj names + and Variable instances. */ + static void markAllDead(); + + bool canSetFormat() const override { return true; } + +private: // Variable overrides + void attachMaybe(QObject *callback, const char *callbackMethod) override; + void fetchMoreChildren() override; + void formatChanged() override; + +private: // Internal + friend class ::CreateVarobjHandler; + friend class ::FetchMoreChildrenHandler; + QString enquotedExpression() const; + void setVarobj(const QString& v); + QString varobj_; + + // How many children should be fetched in one + // increment. + static const int fetchStep = 5; + + /* Map from GDB varobj name to GdbVariable. + FIXME: eventually, should be per-session map. */ + static QMap allVariables_; +}; +} // end of KDevMI + +#endif diff --git a/debuggers/gdb/gdbvariable.cpp b/debuggers/common/mivariable.cpp copy from debuggers/gdb/gdbvariable.cpp copy to debuggers/common/mivariable.cpp --- a/debuggers/gdb/gdbvariable.cpp +++ b/debuggers/common/mivariable.cpp @@ -1,5 +1,5 @@ /* - * GDB-specific Variable + * MI based debugger specific Variable * * Copyright 2009 Vladimir Prus * @@ -19,17 +19,19 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "gdbvariable.h" -#include "gdbcommand.h" -#include "debugsession.h" +#include "mivariable.h" + +#include "midebugsession.h" +#include "mi/micommand.h" -#include #include +#include using namespace KDevelop; -using namespace GDBDebugger; +using namespace KDevMI; +using namespace KDevMI::MI; -QMap GdbVariable::allVariables_; +QMap MIVariable::allVariables_; static bool hasStartedSession() { @@ -44,37 +46,37 @@ && s != IDebugSession::EndedState; } -GdbVariable::GdbVariable(TreeModel* model, TreeItem* parent, - const QString& expression, const QString& display) -: Variable(model, parent, expression, display) +MIVariable::MIVariable(TreeModel* model, TreeItem* parent, + const QString& expression, const QString& display) + : Variable(model, parent, expression, display) { } -GdbVariable::~GdbVariable() +MIVariable::~MIVariable() { if (!varobj_.isEmpty()) { // Delete only top-level variable objects. if (topLevel()) { if (hasStartedSession()) { IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); - s->addCommand(new GDBCommand(GDBMI::VarDelete, - QString("\"%1\"").arg(varobj_))); + MIDebugSession * s = static_cast(is); + s->addCommand(new MICommand(VarDelete, + QString("\"%1\"").arg(varobj_))); } } allVariables_.remove(varobj_); } } -GdbVariable* GdbVariable::findByVarobjName(const QString& varobjName) +MIVariable* MIVariable::findByVarobjName(const QString& varobjName) { if (allVariables_.count(varobjName) == 0) return 0; return allVariables_[varobjName]; } -void GdbVariable::setVarobj(const QString& v) +void MIVariable::setVarobj(const QString& v) { if (!varobj_.isEmpty()) { // this should not happen @@ -89,18 +91,18 @@ static int nextId = 0; -class CreateVarobjHandler : public GDBCommandHandler +class CreateVarobjHandler : public MICommandHandler { public: - CreateVarobjHandler(GdbVariable *variable, QObject *callback, const char *callbackMethod) + CreateVarobjHandler(MIVariable *variable, QObject *callback, const char *callbackMethod) : m_variable(variable), m_callback(callback), m_callbackMethod(callbackMethod) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { if (!m_variable) return; bool hasValue = false; - GdbVariable* variable = m_variable.data(); + MIVariable* variable = m_variable.data(); variable->deleteChildren(); variable->setInScope(true); if (r.reason == "error") { @@ -142,69 +144,69 @@ bool handlesError() override { return true; } private: - QPointer m_variable; + QPointer m_variable; QObject *m_callback; const char *m_callbackMethod; }; -void GdbVariable::attachMaybe(QObject *callback, const char *callbackMethod) +void MIVariable::attachMaybe(QObject *callback, const char *callbackMethod) { if (!varobj_.isEmpty()) return; if (hasStartedSession()) { IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); + MIDebugSession * s = static_cast(is); s->addCommand( - new GDBCommand( - GDBMI::VarCreate, + new MICommand( + VarCreate, QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()), new CreateVarobjHandler(this, callback, callbackMethod))); } } -void GdbVariable::markAllDead() +void MIVariable::markAllDead() { - QMap::iterator i, e; + QMap::iterator i, e; for (i = allVariables_.begin(), e = allVariables_.end(); i != e; ++i) { i.value()->varobj_.clear(); } allVariables_.clear(); } -class FetchMoreChildrenHandler : public GDBCommandHandler +class FetchMoreChildrenHandler : public MICommandHandler { public: - FetchMoreChildrenHandler(GdbVariable *variable, DebugSession *session) + FetchMoreChildrenHandler(MIVariable *variable, MIDebugSession *session) : m_variable(variable), m_session(session), m_activeCommands(1) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { if (!m_variable) return; --m_activeCommands; - GdbVariable* variable = m_variable.data(); + MIVariable* variable = m_variable.data(); if (r.hasField("children")) { - const GDBMI::Value& children = r["children"]; + const Value& children = r["children"]; for (int i = 0; i < children.size(); ++i) { - const GDBMI::Value& child = children[i]; + const Value& child = children[i]; const QString& exp = child["exp"].literal(); if (exp == "public" || exp == "protected" || exp == "private") { ++m_activeCommands; m_session->addCommand( - new GDBCommand(GDBMI::VarListChildren, + new MICommand(VarListChildren, QString("--all-values \"%1\"") .arg(child["name"].literal()), this/*use again as handler*/)); } else { KDevelop::Variable* xvar = m_session->variableController()-> createVariable(variable->model(), variable, child["exp"].literal()); - GdbVariable* var = static_cast(xvar); + MIVariable* var = static_cast(xvar); var->setTopLevel(false); var->setVarobj(child["name"].literal()); bool hasMore = child["numchild"].toInt() != 0 || ( child.hasField("dynamic") && child["dynamic"].toInt()!=0 ); @@ -240,28 +242,28 @@ } private: - QPointer m_variable; - DebugSession *m_session; + QPointer m_variable; + MIDebugSession *m_session; int m_activeCommands; }; -void GdbVariable::fetchMoreChildren() +void MIVariable::fetchMoreChildren() { int c = childItems.size(); // FIXME: should not even try this if app is not started. // Probably need to disable open, or something if (hasStartedSession()) { IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); + MIDebugSession * s = static_cast(is); s->addCommand( - new GDBCommand(GDBMI::VarListChildren, - QString("--all-values \"%1\" %2 %3").arg(varobj_) - .arg( c ).arg( c + fetchStep ), // fetch from .. to .. - new FetchMoreChildrenHandler(this, s))); + new MICommand(VarListChildren, + QString("--all-values \"%1\" %2 %3").arg(varobj_) + .arg( c ).arg( c + fetchStep ), // fetch from .. to .. + new FetchMoreChildrenHandler(this, s))); } } -void GdbVariable::handleUpdate(const GDBMI::Value& var) +void MIVariable::handleUpdate(const Value& var) { if (var.hasField("type_changed") && var["type_changed"].literal() == "true") @@ -271,7 +273,7 @@ setHasMore(var["new_num_children"].toInt() != 0); fetchMoreChildren(); } - + if (var.hasField("in_scope") && var["in_scope"].literal() == "false") { setInScope(false); @@ -290,23 +292,23 @@ delete c; } } - + // FIXME: the below code is essentially copy-paste from // FetchMoreChildrenHandler. We need to introduce GDB-specific // subclass of KDevelop::Variable that is capable of creating // itself from MI output directly, and relay to that. if (var.hasField("new_children")) - { - const GDBMI::Value& children = var["new_children"]; + { + const Value& children = var["new_children"]; for (int i = 0; i < children.size(); ++i) { - const GDBMI::Value& child = children[i]; + const Value& child = children[i]; const QString& exp = child["exp"].literal(); IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); + MIDebugSession * s = static_cast(is); KDevelop::Variable* xvar = s->variableController()-> createVariable(model(), this, exp); - GdbVariable* var = static_cast(xvar); + MIVariable* var = static_cast(xvar); var->setTopLevel(false); var->setVarobj(child["name"].literal()); bool hasMore = child["numchild"].toInt() != 0 || ( child.hasField("dynamic") && child["dynamic"].toInt()!=0 ); @@ -327,53 +329,53 @@ } } -const QString& GdbVariable::varobj() const +const QString& MIVariable::varobj() const { return varobj_; } -QString GdbVariable::enquotedExpression() const +QString MIVariable::enquotedExpression() const { QString expr = expression(); expr.replace('"', "\\\""); expr = expr.prepend('"').append('"'); return expr; } -class SetFormatHandler : public GDBCommandHandler +class SetFormatHandler : public MICommandHandler { public: - SetFormatHandler(GdbVariable *var) + SetFormatHandler(MIVariable *var) : m_variable(var) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { if(r.hasField("value")) m_variable.data()->setValue(r["value"].literal()); } private: - QPointer m_variable; + QPointer m_variable; }; -void GdbVariable::formatChanged() +void MIVariable::formatChanged() { if(childCount()) { foreach(TreeItem* item, childItems) { - Q_ASSERT(dynamic_cast(item)); - if( GdbVariable* var=dynamic_cast(item)) + Q_ASSERT(dynamic_cast(item)); + if( MIVariable* var=dynamic_cast(item)) var->setFormat(format()); } } else { if (hasStartedSession()) { IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); + MIDebugSession * s = static_cast(is); s->addCommand( - new GDBCommand(GDBMI::VarSetFormat, + new MICommand(VarSetFormat, QString(" \"%1\" %2 ").arg(varobj_).arg(format2str(format())), new SetFormatHandler(this) ) diff --git a/debuggers/gdb/variablecontroller.h b/debuggers/common/mivariablecontroller.h copy from debuggers/gdb/variablecontroller.h copy to debuggers/common/mivariablecontroller.h --- a/debuggers/gdb/variablecontroller.h +++ b/debuggers/common/mivariablecontroller.h @@ -1,5 +1,5 @@ /* - * GDB Debugger Support + * Variable controller implementation common to MI based debugger * * Copyright 2007 Hamish Rodda * Copyright 2008 Vladimir Prus @@ -21,57 +21,56 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef GDBDEBUGGER_VARIABLECONTROLLER_H -#define GDBDEBUGGER_VARIABLECONTROLLER_H +#ifndef MIVARIABLECONTROLLER_H +#define MIVARIABLECONTROLLER_H -#include +#include "dbgglobal.h" -#include "gdbglobal.h" +#include -using namespace KDevelop; +namespace KDevMI { -namespace GDBMI { +namespace MI { struct AsyncRecord; struct ResultRecord; struct Value; } -namespace GDBDebugger { - -class GDBController; -class DebugSession; - -class VariableController : public KDevelop::IVariableController +class MIDebugSession; +class MIVariableController : public KDevelop::IVariableController { Q_OBJECT public: - VariableController(DebugSession* parent); + MIVariableController( MIDebugSession* parent); + + KDevelop::Variable* createVariable(KDevelop::TreeModel* model, KDevelop::TreeItem* parent, + const QString& expression, + const QString& display = "") override; + + KTextEditor::Range expressionRangeUnderCursor(KTextEditor::Document* doc, + const KTextEditor::Cursor& cursor) override; - Variable* createVariable(TreeModel* model, TreeItem* parent, - const QString& expression, - const QString& display = "") override; - KTextEditor::Range expressionRangeUnderCursor(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor) override; void addWatch(KDevelop::Variable* variable) override; void addWatchpoint(KDevelop::Variable* variable) override; void update() override; private slots: - void programStopped(const GDBMI::AsyncRecord &r); + void programStopped(const MI::AsyncRecord &r); void stateChanged(KDevelop::IDebugSession::DebuggerState); private: - DebugSession* debugSession() const; + MIDebugSession* debugSession() const; void updateLocals(); - void handleVarUpdate(const GDBMI::ResultRecord& r); - void addWatch(const GDBMI::ResultRecord& r); - void addWatchpoint(const GDBMI::ResultRecord& r); + void handleVarUpdate(const MI::ResultRecord& r); + void addWatch(const MI::ResultRecord& r); + void addWatchpoint(const MI::ResultRecord& r); - void handleEvent(IDebugSession::event_t event) override; + void handleEvent(KDevelop::IDebugSession::event_t event) override; }; -} +} // end of namespace KDevMI -#endif // GDBDEBUGGER_VARIABLECONTROLLER_H +#endif // MIVARIABLECONTROLLER_H diff --git a/debuggers/gdb/variablecontroller.cpp b/debuggers/common/mivariablecontroller.cpp copy from debuggers/gdb/variablecontroller.cpp copy to debuggers/common/mivariablecontroller.cpp --- a/debuggers/gdb/variablecontroller.cpp +++ b/debuggers/common/mivariablecontroller.cpp @@ -21,41 +21,48 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "variablecontroller.h" +#include "mivariablecontroller.h" -#include +#include "debuglog.h" +#include "midebugsession.h" +#include "mivariable.h" +#include "mi/mi.h" +#include "mi/micommand.h" +#include "stringhelpers.h" + +#include #include +#include #include #include -#include - -#include "gdbcommand.h" -#include "debugsession.h" -#include "stringhelpers.h" -#include "gdbvariable.h" -#include "debug.h" #include -using namespace GDBDebugger; using namespace KDevelop; - -VariableController::VariableController(DebugSession* parent) - : KDevelop::IVariableController(parent) +using namespace KDevMI; +using namespace KDevMI::MI; +using KTextEditor::Cursor; +using KTextEditor::Document; +using KTextEditor::Range; + +MIVariableController::MIVariableController(MIDebugSession *parent) + : IVariableController(parent) { Q_ASSERT(parent); - connect(parent, &DebugSession::programStopped, this, &VariableController::programStopped); - connect(parent, &DebugSession::stateChanged, this, &VariableController::stateChanged); + connect(parent, &MIDebugSession::inferiorStopped, + this, &MIVariableController::programStopped); + connect(parent, &MIDebugSession::stateChanged, + this, &MIVariableController::stateChanged); } -DebugSession *VariableController::debugSession() const +MIDebugSession *MIVariableController::debugSession() const { - return static_cast(const_cast(QObject::parent())); + return static_cast(const_cast(QObject::parent())); } -void VariableController::programStopped(const GDBMI::AsyncRecord& r) +void MIVariableController::programStopped(const AsyncRecord& r) { - if (debugSession()->stateIsOn(s_shuttingDown)) return; + if (debugSession()->debuggerStateIsOn(s_shuttingDown)) return; if (r.hasField("reason") && r["reason"].literal() == "function-finished" && r.hasField("gdb-result-var")) @@ -66,7 +73,7 @@ } } -void VariableController::update() +void MIVariableController::update() { qCDebug(DEBUGGERGDB) << autoUpdate(); if (autoUpdate() & UpdateWatches) { @@ -81,38 +88,39 @@ ((autoUpdate() & UpdateWatches) && variableCollection()->watches()->childCount() > 0)) { debugSession()->addCommand( - new GDBCommand(GDBMI::VarUpdate, "--all-values *", this, - &VariableController::handleVarUpdate)); + new MICommand(VarUpdate, "--all-values *", this, + &MIVariableController::handleVarUpdate)); } } -void VariableController::handleVarUpdate(const GDBMI::ResultRecord& r) +void MIVariableController::handleVarUpdate(const ResultRecord& r) { - const GDBMI::Value& changed = r["changelist"]; + const Value& changed = r["changelist"]; for (int i = 0; i < changed.size(); ++i) { - const GDBMI::Value& var = changed[i]; - GdbVariable* v = GdbVariable::findByVarobjName(var["name"].literal()); + const Value& var = changed[i]; + MIVariable* v = MIVariable::findByVarobjName(var["name"].literal()); // v can be NULL here if we've already removed locals after step, // but the corresponding -var-delete command is still in the queue. if (v) { v->handleUpdate(var); } } } -class StackListArgumentsHandler : public GDBCommandHandler + +class StackListArgumentsHandler : public MICommandHandler { public: StackListArgumentsHandler(QStringList localsName) : m_localsName(localsName) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { if (!KDevelop::ICore::self()->debugController()) return; //happens on shutdown // FIXME: handle error. - const GDBMI::Value& locals = r["stack-args"][0]["args"]; + const Value& locals = r["stack-args"][0]["args"]; for (int i = 0; i < locals.size(); i++) { m_localsName << locals[i].literal(); @@ -128,42 +136,42 @@ QStringList m_localsName; }; -class StackListLocalsHandler : public GDBCommandHandler +class StackListLocalsHandler : public MICommandHandler { public: - StackListLocalsHandler(DebugSession *session) + StackListLocalsHandler(MIDebugSession *session) : m_session(session) {} - void handle(const GDBMI::ResultRecord &r) override + void handle(const ResultRecord &r) override { // FIXME: handle error. - const GDBMI::Value& locals = r["locals"]; + const Value& locals = r["locals"]; QStringList localsName; for (int i = 0; i < locals.size(); i++) { - const GDBMI::Value& var = locals[i]; + const Value& var = locals[i]; localsName << var["name"].literal(); } int frame = m_session->frameStackModel()->currentFrame(); m_session->addCommand( //dont'show value, low-frame, high-frame - new GDBCommand(GDBMI::StackListArguments, QString("0 %1 %2").arg(frame).arg(frame), - new StackListArgumentsHandler(localsName))); + new MICommand(StackListArguments, QString("0 %1 %2").arg(frame).arg(frame), + new StackListArgumentsHandler(localsName))); } private: - DebugSession *m_session; + MIDebugSession *m_session; }; -void VariableController::updateLocals() +void MIVariableController::updateLocals() { debugSession()->addCommand( - new GDBCommand(GDBMI::StackListLocals, "--simple-values", - new StackListLocalsHandler(debugSession()))); + new MICommand(StackListLocals, "--simple-values", + new StackListLocalsHandler(debugSession()))); } -KTextEditor::Range VariableController::expressionRangeUnderCursor(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor) +Range MIVariableController::expressionRangeUnderCursor(Document* doc, const Cursor& cursor) { QString line = doc->line(cursor.line()); int index = cursor.column(); @@ -182,75 +190,71 @@ if (!(start < end)) return {}; - return { KTextEditor::Cursor{cursor.line(), start}, KTextEditor::Cursor{cursor.line(), end} }; + return { Cursor{cursor.line(), start}, Cursor{cursor.line(), end} }; } -void VariableController::addWatch(KDevelop::Variable* variable) +void MIVariableController::addWatch(KDevelop::Variable* variable) { // FIXME: should add async 'get full expression' method - // to GdbVariable, not poke at varobj. In that case, + // to MIVariable, not poke at varobj. In that case, // we will be able to make addWatch a generic method, not // gdb-specific one. - if (GdbVariable *gv = dynamic_cast(variable)) + if (MIVariable *gv = dynamic_cast(variable)) { debugSession()->addCommand( - new GDBCommand(GDBMI::VarInfoPathExpression, + new MICommand(VarInfoPathExpression, gv->varobj(), this, - &VariableController::addWatch)); + &MIVariableController::addWatch)); } } -void VariableController::addWatchpoint(KDevelop::Variable* variable) +void MIVariableController::addWatchpoint(KDevelop::Variable* variable) { // FIXME: should add async 'get full expression' method - // to GdbVariable, not poke at varobj. In that case, + // to MIVariable, not poke at varobj. In that case, // we will be able to make addWatchpoint a generic method, not // gdb-specific one. - if (GdbVariable *gv = dynamic_cast(variable)) + if (MIVariable *gv = dynamic_cast(variable)) { debugSession()->addCommand( - new GDBCommand(GDBMI::VarInfoPathExpression, + new MICommand(VarInfoPathExpression, gv->varobj(), this, - &VariableController::addWatchpoint)); + &MIVariableController::addWatchpoint)); } } -void VariableController::addWatch(const GDBMI::ResultRecord& r) +void MIVariableController::addWatch(const ResultRecord& r) { // FIXME: handle error. if (r.reason == "done" && !r["path_expr"].literal().isEmpty()) { variableCollection()->watches()->add(r["path_expr"].literal()); } } -void VariableController::addWatchpoint(const GDBMI::ResultRecord& r) +void MIVariableController::addWatchpoint(const ResultRecord& r) { if (r.reason == "done" && !r["path_expr"].literal().isEmpty()) { KDevelop::ICore::self()->debugController()->breakpointModel()->addWatchpoint(r["path_expr"].literal()); } } -KDevelop::Variable* VariableController:: -createVariable(TreeModel* model, TreeItem* parent, - const QString& expression, const QString& display) +Variable* MIVariableController::createVariable(TreeModel* model, TreeItem* parent, + const QString& expression, const QString& display) { - return new GdbVariable(model, parent, expression, display); + return new MIVariable(model, parent, expression, display); } -void VariableController::handleEvent(IDebugSession::event_t event) +void MIVariableController::handleEvent(IDebugSession::event_t event) { IVariableController::handleEvent(event); } -void VariableController::stateChanged(IDebugSession::DebuggerState state) +void MIVariableController::stateChanged(IDebugSession::DebuggerState state) { if (state == IDebugSession::EndedState) { - GdbVariable::markAllDead(); + MIVariable::markAllDead(); } } - - - diff --git a/debuggers/gdb/stringhelpers.h b/debuggers/common/stringhelpers.h rename from debuggers/gdb/stringhelpers.h rename to debuggers/common/stringhelpers.h --- a/debuggers/gdb/stringhelpers.h +++ b/debuggers/common/stringhelpers.h @@ -19,11 +19,7 @@ #ifndef __STRINGHELPERS_H__ #define __STRINGHELPERS_H__ -#include - -class QString; -class QChar; -class QStringList; +#include namespace Utils { @@ -33,5 +29,10 @@ * */ int expressionAt( const QString& contents, int index ); -} -#endif +QString quoteExpression(QString expr); + +QString unquoteExpression(QString expr); + +} // end of namespace Utils + +#endif // __STRINGHELPERS_H__ diff --git a/debuggers/gdb/stringhelpers.cpp b/debuggers/common/stringhelpers.cpp rename from debuggers/gdb/stringhelpers.cpp rename to debuggers/common/stringhelpers.cpp --- a/debuggers/gdb/stringhelpers.cpp +++ b/debuggers/common/stringhelpers.cpp @@ -165,5 +165,20 @@ return index; } +QString quoteExpression(QString expr) +{ + expr.replace('"', "\\\""); + expr = expr.prepend('"').append('"'); + return expr; +} +QString unquoteExpression(QString expr) +{ + if (expr.left(1) == QString('"') && expr.right(1) == QString('"')) { + expr = expr.mid(1, expr.length()-2); + expr.replace("\\\"", "\""); + } + return expr; } + +} // end of namespace Utils diff --git a/debuggers/gdb/stty.h b/debuggers/common/stty.h rename from debuggers/gdb/stty.h rename to debuggers/common/stty.h --- a/debuggers/gdb/stty.h +++ b/debuggers/common/stty.h @@ -32,8 +32,7 @@ #include #include -namespace GDBDebugger -{ +namespace KDevMI { class STTY : public QObject { @@ -71,6 +70,6 @@ char tty_slave[50]; // "/dev/ttyxx" | "/dev/pts/########..." }; -} +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/stty.cpp b/debuggers/common/stty.cpp rename from debuggers/gdb/stty.cpp rename to debuggers/common/stty.cpp --- a/debuggers/gdb/stty.cpp +++ b/debuggers/common/stty.cpp @@ -71,13 +71,12 @@ #include #include "stty.h" -#include "debug.h" +#include "debuglog.h" #define PTY_FILENO 3 #define BASE_CHOWN "konsole_grantpty" -namespace GDBDebugger -{ +using namespace KDevMI; static int chownpty(int fd, int grant) // param fd: the fd of a master pty. @@ -343,5 +342,4 @@ } return true; } -} // ************************************************************************** diff --git a/debuggers/gdb/CMakeLists.txt b/debuggers/gdb/CMakeLists.txt --- a/debuggers/gdb/CMakeLists.txt +++ b/debuggers/gdb/CMakeLists.txt @@ -35,23 +35,14 @@ ########### next target ############### -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") - set(kdevgdb_SRCS debuggerplugin.cpp gdb.cpp - gdbcommandqueue.cpp - gdbcommand.cpp - stty.cpp disassemblewidget.cpp gdboutputwidget.cpp # debuggertracingdialog.cpp - breakpointcontroller.cpp - mi/gdbmi.cpp - mi/milexer.cpp - mi/miparser.cpp - stringhelpers.cpp debugsession.cpp + gdbbreakpointcontroller.cpp gdbconfigpage.cpp debugjob.cpp selectcoredialog.cpp @@ -102,6 +93,7 @@ KDev::Project KDev::Util KF5::TextEditor + kdevdebuggercommon ) if(KF5SysGuard_FOUND) target_link_libraries(kdevgdb KF5::ProcessUi) @@ -121,6 +113,7 @@ KF5::KIOWidgets KF5::TextEditor KF5::Parts + kdevdebuggercommon ) if(KF5SysGuard_FOUND) target_link_libraries(test_gdb KF5::ProcessUi) diff --git a/debuggers/gdb/debug.h b/debuggers/gdb/debug.h deleted file mode 100644 --- a/debuggers/gdb/debug.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef KDEVELOP_GDB_DEBUG_H -#define KDEVELOP_GDB_DEBUG_H - -#include -Q_DECLARE_LOGGING_CATEGORY(DEBUGGERGDB) - -#endif diff --git a/debuggers/gdb/debuggerplugin.h b/debuggers/gdb/debuggerplugin.h --- a/debuggers/gdb/debuggerplugin.h +++ b/debuggers/gdb/debuggerplugin.h @@ -49,7 +49,8 @@ class ProcessLineMaker; } -namespace GDBDebugger +namespace KDevMI { +namespace GDB { class DisassembleWidget; class Breakpoint; @@ -145,6 +146,7 @@ DebuggerToolFactory< MemoryViewerWidget >* memoryviewerfactory; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/debuggerplugin.cpp b/debuggers/gdb/debuggerplugin.cpp --- a/debuggers/gdb/debuggerplugin.cpp +++ b/debuggers/gdb/debuggerplugin.cpp @@ -70,19 +70,16 @@ #endif #include "memviewdlg.h" #include "gdboutputwidget.h" -#include "gdbglobal.h" +#include "dbgglobal.h" #include "debugsession.h" #include "selectcoredialog.h" #include #include "gdbconfigpage.h" #include "debugjob.h" -namespace GDBDebugger -{ - -K_PLUGIN_FACTORY_WITH_JSON(CppDebuggerFactory, "kdevgdb.json", registerPlugin(); ) +namespace KDevMI { namespace GDB { template class DebuggerToolFactory : public KDevelop::IToolViewFactory @@ -127,6 +124,13 @@ Qt::DockWidgetArea m_defaultArea; }; +} // end of namespace GDB +} // end of namespace KDevMI + +using namespace KDevMI::GDB; + +K_PLUGIN_FACTORY_WITH_JSON(CppDebuggerFactory, "kdevgdb.json", registerPlugin(); ) + CppDebuggerPlugin::CppDebuggerPlugin( QObject *parent, const QVariantList & ) : KDevelop::IPlugin( "kdevgdb", parent ), m_config(KSharedConfig::openConfig(), "GDB Debugger") @@ -325,7 +329,8 @@ connect(session, &DebugSession::showMessage, this, &CppDebuggerPlugin::controllerMessage); connect(session, &DebugSession::reset, this, &CppDebuggerPlugin::reset); connect(session, &DebugSession::finished, this, &CppDebuggerPlugin::slotFinished); - connect(session, &DebugSession::raiseGdbConsoleViews, this, &CppDebuggerPlugin::raiseGdbConsoleViews); + connect(session, &DebugSession::raiseDebuggerConsoleViews, + this, &CppDebuggerPlugin::raiseGdbConsoleViews); return session; } @@ -407,6 +412,4 @@ emit showMessage(this, msg, timeout); } -} - #include "debuggerplugin.moc" diff --git a/debuggers/gdb/debuggertracingdialog.h b/debuggers/gdb/debuggertracingdialog.h --- a/debuggers/gdb/debuggertracingdialog.h +++ b/debuggers/gdb/debuggertracingdialog.h @@ -16,7 +16,8 @@ #include "ui_debuggertracingdialog.h" #include -namespace GDBDebugger +namespace KDevMI { +namespace GDB { class Breakpoint; @@ -36,6 +37,8 @@ private: Breakpoint* bp_; }; -} + +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/debuggertracingdialog.cpp b/debuggers/gdb/debuggertracingdialog.cpp --- a/debuggers/gdb/debuggertracingdialog.cpp +++ b/debuggers/gdb/debuggertracingdialog.cpp @@ -31,100 +31,97 @@ /* WARNING: this code was not yet ported to KDevelop4 and is unused, but is intended to be ported. */ -namespace GDBDebugger +using namespace KDevMI::GDB; + +DebuggerTracingDialog +::DebuggerTracingDialog(Breakpoint* bp, + QWidget* parent) +: QDialog(parent), bp_(bp) { + setupUi(this); - DebuggerTracingDialog - ::DebuggerTracingDialog(Breakpoint* bp, - QWidget* parent) - : QDialog(parent), bp_(bp) - { - setupUi(this); + expressions->setButtons(KEditListBox::Add | KEditListBox::Remove); - expressions->setButtons(KEditListBox::Add | KEditListBox::Remove); + connect(enable, SIGNAL(stateChanged(int)), + this, SLOT(enableOrDisable(int))); - connect(enable, SIGNAL(stateChanged(int)), - this, SLOT(enableOrDisable(int))); + connect(enableCustomFormat, SIGNAL(stateChanged(int)), + this, SLOT(enableOrDisableCustomFormat(int))); - connect(enableCustomFormat, SIGNAL(stateChanged(int)), - this, SLOT(enableOrDisableCustomFormat(int))); + enable->setChecked(bp_->tracingEnabled()); + expressions->setItems(bp_->tracedExpressions()); + enableCustomFormat->setChecked(bp_->traceFormatStringEnabled()); + customFormat->setText(bp_->traceFormatString()); - enable->setChecked(bp_->tracingEnabled()); - expressions->setItems(bp_->tracedExpressions()); - enableCustomFormat->setChecked(bp_->traceFormatStringEnabled()); - customFormat->setText(bp_->traceFormatString()); + enableOrDisable(enable->isChecked()); - enableOrDisable(enable->isChecked()); + // Go away if the breakpoint does + connect(bp_, SIGNAL(destroyed(QObject*)), this, SLOT(reject())); +} - // Go away if the breakpoint does - connect(bp_, SIGNAL(destroyed(QObject*)), this, SLOT(reject())); - } +void DebuggerTracingDialog::enableOrDisable(int state) +{ + bool enable = (state == Qt::Checked); - void DebuggerTracingDialog::enableOrDisable(int state) - { - bool enable = (state == Qt::Checked); + expressionsLabel->setEnabled(enable); + expressions->setEnabled(enable); + enableCustomFormat->setEnabled(enable); + customFormat->setEnabled(enable && enableCustomFormat->isChecked()); +} - expressionsLabel->setEnabled(enable); - expressions->setEnabled(enable); - enableCustomFormat->setEnabled(enable); - customFormat->setEnabled(enable && enableCustomFormat->isChecked()); - } +void DebuggerTracingDialog::enableOrDisableCustomFormat(int state) +{ + customFormat->setEnabled(state == Qt::Checked); +} - void DebuggerTracingDialog::enableOrDisableCustomFormat(int state) - { - customFormat->setEnabled(state == Qt::Checked); - } +void DebuggerTracingDialog::accept() +{ + // Check that if we use format string, + // the number of expression is not larget than the number of + // format specifiers + bool ok = true; - void DebuggerTracingDialog::accept() + if (enableCustomFormat->isChecked()) { - // Check that if we use format string, - // the number of expression is not larget than the number of - // format specifiers - bool ok = true; - - if (enableCustomFormat->isChecked()) - { - QString s = customFormat->text(); - int percent_count = 0; - for (int i = 0; i < s.length(); ++i) - if (s[i] == '%') + QString s = customFormat->text(); + int percent_count = 0; + for (int i = 0; i < s.length(); ++i) + if (s[i] == '%') + { + if (i+1 < s.length()) { - if (i+1 < s.length()) + if (s[i+1] != '%') + { + ++percent_count; + } + else { - if (s[i+1] != '%') - { - ++percent_count; - } - else - { - // Double % - ++i; - } + // Double % + ++i; } } - - if (percent_count < expressions->items().count()) - { - ok = false; - - KMessageBox::error( - this, - "Not enough format specifiers" - "

The number of format specifiers in the custom format " - "string is less than the number of expressions. Either remove " - "some expressions or edit the format string.", - "Not enough format specifiers"); } - } - if (ok) + if (percent_count < expressions->items().count()) { - bp_->setTracingEnabled(enable->isChecked()); - bp_->setTracedExpressions(expressions->items()); - bp_->setTraceFormatStringEnabled(enableCustomFormat->isChecked()); - bp_->setTraceFormatString(customFormat->text()); - QDialog::accept(); + ok = false; + + KMessageBox::error( + this, + "Not enough format specifiers" + "

The number of format specifiers in the custom format " + "string is less than the number of expressions. Either remove " + "some expressions or edit the format string.", + "Not enough format specifiers"); } } + if (ok) + { + bp_->setTracingEnabled(enable->isChecked()); + bp_->setTracedExpressions(expressions->items()); + bp_->setTraceFormatStringEnabled(enableCustomFormat->isChecked()); + bp_->setTraceFormatString(customFormat->text()); + QDialog::accept(); + } } diff --git a/debuggers/gdb/debugjob.h b/debuggers/gdb/debugjob.h --- a/debuggers/gdb/debugjob.h +++ b/debuggers/gdb/debugjob.h @@ -32,7 +32,8 @@ class ILaunchConfiguration; } -namespace GDBDebugger +namespace KDevMI { +namespace GDB { class CppDebuggerPlugin; class DebugSession; @@ -42,7 +43,7 @@ { Q_OBJECT public: - DebugJob( GDBDebugger::CppDebuggerPlugin* p, KDevelop::ILaunchConfiguration* launchcfg, + DebugJob( CppDebuggerPlugin* p, KDevelop::ILaunchConfiguration* launchcfg, IExecutePlugin* plugin, QObject* parent = 0 ); void start() override; protected: @@ -73,6 +74,7 @@ void sessionFinished(); }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/debugjob.cpp b/debuggers/gdb/debugjob.cpp --- a/debuggers/gdb/debugjob.cpp +++ b/debuggers/gdb/debugjob.cpp @@ -23,33 +23,34 @@ #include "debugjob.h" #include "debuggerplugin.h" -#include -#include +#include "debuglog.h" +#include "debugsession.h" + +#include #include #include #include -#include +#include #include -#include -#include "debugsession.h" -#include "debug.h" +#include + +#include #include -#include -using namespace GDBDebugger; +using namespace KDevMI::GDB; using namespace KDevelop; -DebugJob::DebugJob( GDBDebugger::CppDebuggerPlugin* p, KDevelop::ILaunchConfiguration* launchcfg, IExecutePlugin* execute, QObject* parent) +DebugJob::DebugJob(CppDebuggerPlugin* p, KDevelop::ILaunchConfiguration* launchcfg, IExecutePlugin* execute, QObject* parent) : KDevelop::OutputJob(parent) , m_launchcfg( launchcfg ) , m_execute( execute ) { setCapabilities(Killable); m_session = p->createSession(); - connect(m_session, &DebugSession::applicationStandardOutputLines, this, &DebugJob::stderrReceived); - connect(m_session, &DebugSession::applicationStandardErrorLines, this, &DebugJob::stdoutReceived); + connect(m_session, &DebugSession::inferiorStdoutLines, this, &DebugJob::stderrReceived); + connect(m_session, &DebugSession::inferiorStderrLines, this, &DebugJob::stdoutReceived); connect(m_session, &DebugSession::finished, this, &DebugJob::done ); if (launchcfg->project()) { @@ -101,7 +102,7 @@ setModel(model); setTitle(m_launchcfg->name()); - QString startWith = grp.readEntry(GDBDebugger::startWithEntry, QString("ApplicationOutput")); + QString startWith = grp.readEntry(startWithEntry, QString("ApplicationOutput")); if (startWith == "GdbConsole") { setVerbosity(Silent); } else if (startWith == "FrameStack") { @@ -112,7 +113,7 @@ startOutput(); - if (!m_session->startProgram( m_launchcfg, m_execute )) { + if (!m_session->startDebugging(m_launchcfg, m_execute)) { done(); } } diff --git a/debuggers/gdb/debugsession.h b/debuggers/gdb/debugsession.h --- a/debuggers/gdb/debugsession.h +++ b/debuggers/gdb/debugsession.h @@ -26,281 +26,72 @@ #ifndef GDB_DEBUGSESSION_H #define GDB_DEBUGSESSION_H -#include -#include +#include "midebugsession.h" + +#include "dbgglobal.h" +#include "gdb.h" +#include "gdbbreakpointcontroller.h" +#include "gdbframestackmodel.h" +#include "variablecontroller.h" +#include "mi/mi.h" + #include -#include +#include +#include -#include "breakpointcontroller.h" -#include "gdbglobal.h" -#include "mi/gdbmi.h" class IExecutePlugin; class KToolBar; -namespace KTextEditor { -class Cursor; -} namespace KDevelop { class ProcessLineMaker; class ILaunchConfiguration; } -namespace GDBDebugger { +namespace KDevMI { class STTY; + +namespace MI { +class MICommand; class CommandQueue; -class GDBCommand; -class GDB; +} -static const char gdbPathEntry[] = "GDB Path"; -static const char debuggerShellEntry[] = "Debugger Shell"; -static const char remoteGdbConfigEntry[] = "Remote GDB Config Script"; -static const char remoteGdbShellEntry[] = "Remote GDB Shell Script"; -static const char remoteGdbRunEntry[] = "Remote GDB Run Script"; -static const char staticMembersEntry[] = "Display Static Members"; -static const char demangleNamesEntry[] = "Display Demangle Names"; -static const char allowForcedBPEntry[] = "Allow Forced Breakpoint Set"; -static const char startWithEntry[] = "Start With"; +namespace GDB { -class DebugSession : public KDevelop::IDebugSession +class DebugSession : public MIDebugSession { Q_OBJECT public: DebugSession(); ~DebugSession() override; - DebuggerState state() const override; - - bool restartAvaliable() const override; - - BreakpointController* breakpointController() const override; - KDevelop::IVariableController* variableController() const override; - KDevelop::IFrameStackModel* frameStackModel() const override; - - bool hasCrashed() const; - - using IDebugSession::event; -Q_SIGNALS: - void applicationStandardOutputLines(const QStringList& lines); - void applicationStandardErrorLines(const QStringList& lines); - void showMessage(const QString& message, int timeout); - void reset(); - void programStopped(const GDBMI::AsyncRecord& mi_record); - -public Q_SLOTS: - /** - * Start the debugger, and execute the program specified by \a run. - */ - bool startProgram(KDevelop::ILaunchConfiguration* run, IExecutePlugin* execute); - void restartDebugger() override; - void stopDebugger() override; - void interruptDebugger() override; - void run() override; - void runToCursor() override; - void jumpToCursor() override; - void stepOver() override; - void stepIntoInstruction() override; - void stepInto() override; - void stepOverInstruction() override; - void stepOut() override; - - /** - * Start the debugger and examine the core file given by \a coreFile. - */ - void examineCoreFile(const QUrl& debugee, const QUrl& coreFile); - - /** - * Attach to currently running process with the given \a pid. - */ - void attachToProcess(int pid); + BreakpointController * breakpointController() const override; + VariableController * variableController() const override; + GdbFrameStackModel * frameStackModel() const override; protected: - /** - * Testing mode affects a (very!) limited number of settings in an attempt to create - * a cleaner and more reproducible environment for unit tests. - */ - void setTesting(bool testing); + GdbDebugger *createDebugger() const override; + void initializeDebugger() override; + bool execInferior(KDevelop::ILaunchConfiguration *cfg, const QString &executable) override; -Q_SIGNALS: - void raiseGdbConsoleViews(); + void configure(KDevelop::ILaunchConfiguration *cfg); private Q_SLOTS: - void slotDebuggerAbnormalExit(); - -private: - void _gdbStateChanged(DBGStateFlags oldState, DBGStateFlags newState); - - void setupController(); - void setSessionState(KDevelop::IDebugSession::DebuggerState state); - -public: - /** - * Run currently executing program to the given \a url and \a line. - */ - void runUntil(const QUrl& url, int line); - - /** - * Run currently executing program to the given \a address - */ - void runUntil(const QString& address); - /** - * Move the execution point of the currently executing program to the given \a url and \a line. - */ - void jumpTo(const QUrl& url, int line); - - /** - * Move the execution point of the currently executing program to the given \a address. - *Note: It can be really very dangerous, so use jumpTo instead. - */ - void jumpToMemoryAddress(const QString& address); - - /** Adds a command to the end of queue of commands to be executed - by gdb. The command will be actually sent to gdb only when - replies from all previous commands are received and full processed. - - The literal command sent to gdb is obtained by calling - cmd->cmdToSend. The call is made immediately before sending the - command, so it's possible to use results of prior commands when - computing the exact command to send. - */ - void addCommand(GDBCommand* cmd); - - /** Same as above, but internally constructs new GDBCommand - instance from the string. */ - void addCommand(GDBMI::CommandType type, const QString& cmd = QString()); - - bool stateIsOn(DBGStateFlags state) const; - DBGStateFlags debuggerState() const; - - using QObject::event; - -private: - /** Try to execute next command in the queue. If GDB is not - busy with previous command, and there's a command in the - queue, sends it. */ - void executeCmd(); - void ensureGdbListening(); - void destroyCmds(); - - /** Called when there are no pending commands and 'state_reload_needed' - is true. Also can be used to immediately reload program state. - Issues commands to completely reload all program state shown - to the user. - */ - void reloadProgramState(); - - void programNoApp(const QString &msg); - void programFinished(const QString &msg); - - void setStateOn(DBGStateFlags stateOn); - void setStateOff(DBGStateFlags stateOff); - void setState(DBGStateFlags newState); - - void debugStateChange(DBGStateFlags oldState, DBGStateFlags newState); - - void raiseEvent(event_t e) override; - - bool startDebugger(KDevelop::ILaunchConfiguration* cfg); - -private Q_SLOTS: - - void gdbReady(); - - void gdbExited(); - - void slotProgramStopped(const GDBMI::AsyncRecord& mi_record); - - /** Default handler for errors. - Tries to guess is the error message is telling that target is - gone, if so, informs the user. - Otherwise, shows a dialog box and reloads view state. */ - void defaultErrorHandler(const GDBMI::ResultRecord& result); - - /**Triggered every time program begins/continues it's execution.*/ - void programRunning(); - - /** Handle MI async notifications. */ - void processNotification(const GDBMI::AsyncRecord& n); - - // All of these slots are entered in the controller's thread, as they use queued connections or are called internally - void queueCmd(GDBCommand *cmd); - - void configure(); - - // Pops up a dialog box with some hopefully - // detailed information about which state debugger - // is in, which commands were sent and so on. - void explainDebuggerStatus(); - - void slotKillGdb(); - void handleVersion(const QStringList& s); - void handleFileExecAndSymbols(const GDBMI::ResultRecord& r); - void handleTargetAttach(const GDBMI::ResultRecord& r); - void handleCoreFile(const GDBMI::ResultRecord& r); - -public Q_SLOTS: - void slotKill(); - void slotUserGDBCmd(const QString&); - -Q_SIGNALS: - void rawGDBMemoryDump (char *buf); - void rawGDBRegisters (char *buf); - void rawGDBLibraries (char *buf); - void ttyStdout (const QByteArray& output); - void ttyStderr (const QByteArray& output); - void gdbInternalCommandStdout (const QString& output); - void gdbUserCommandStdout (const QString& output); - void gdbStateChanged(DBGStateFlags oldState, DBGStateFlags newState); - - void debuggerAbnormalExit(); - - - /** Emitted immediately after breakpoint is hit, before any commands - are sent and before current line indicator is shown. */ - void breakpointHit(int id); - /** Emitted for watchpoint hit, after line indicator is shown. */ - void watchpointHit(int id, - const QString& oldValue, const QString& newValue); + void handleFileExecAndSymbols(const MI::ResultRecord& r); private: friend class GdbTest; - BreakpointController* m_breakpointController; - KDevelop::IVariableController* m_variableController; - KDevelop::IFrameStackModel* m_frameStackModel; - - KDevelop::ProcessLineMaker *m_procLineMaker; - KDevelop::ProcessLineMaker *m_gdbLineMaker; - DebuggerState m_sessionState; - KConfigGroup m_config; - QPointer m_gdb; - bool m_testing; - - CommandQueue* commandQueue_; - - QScopedPointer m_tty; - QString badCore_; - - // Some state variables - DBGStateFlags state_; - - /**When program stops and all commands from queue are executed and this variable is true, program state shown to the user is updated.*/ - bool state_reload_needed; - /**True if program has stopped and all stuff like breakpoints is being updated.*/ - bool stateReloadInProgress_; - /**True if process crashed*/ - bool m_hasCrashed; - - QTime commandExecutionTime; - - ///Exit code of the last inferior(in format: exit normally, with code "number" e.t.c) - QString m_inferiorExitCode; + BreakpointController *m_breakpointController; + VariableController *m_variableController; + GdbFrameStackModel *m_frameStackModel; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/debugsession.cpp b/debuggers/gdb/debugsession.cpp --- a/debuggers/gdb/debugsession.cpp +++ b/debuggers/gdb/debugsession.cpp @@ -25,1450 +25,217 @@ #include "debugsession.h" -#include +#include "debuglog.h" +#include "gdb.h" +#include "gdbbreakpointcontroller.h" +#include "gdbframestackmodel.h" +#include "mi/micommand.h" +#include "stty.h" +#include "variablecontroller.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "breakpointcontroller.h" -#include "variablecontroller.h" -#include "gdb.h" -#include "gdbcommandqueue.h" -#include "stty.h" -#include "gdbframestackmodel.h" -#include "debug.h" +#include +#include +#include +#include +#include +using namespace KDevMI::GDB; +using namespace KDevMI::MI; using namespace KDevelop; -namespace GDBDebugger { - DebugSession::DebugSession() - : m_breakpointController(nullptr) + : MIDebugSession() + , m_breakpointController(nullptr) , m_variableController(nullptr) , m_frameStackModel(nullptr) - , m_sessionState(NotStartedState) - , m_config(KSharedConfig::openConfig(), "GDB Debugger") - , m_testing(false) - , commandQueue_(new CommandQueue) - , m_tty(0) - , state_(s_dbgNotStarted | s_appNotStarted) - , state_reload_needed(false) - , stateReloadInProgress_(false) - , m_hasCrashed(false) { - configure(); - m_breakpointController = new BreakpointController(this); m_variableController = new VariableController(this); m_frameStackModel = new GdbFrameStackModel(this); - - m_procLineMaker = new KDevelop::ProcessLineMaker(this); - - connect(m_procLineMaker, &ProcessLineMaker::receivedStdoutLines, - this, &DebugSession::applicationStandardOutputLines); - connect(m_procLineMaker, &ProcessLineMaker::receivedStderrLines, - this, &DebugSession::applicationStandardErrorLines); - setupController(); } -// Deleting the session involves shutting down gdb nicely. -// When were attached to a process, we must first detach so that the process -// can continue running as it was before being attached. gdb is quite slow to -// detach from a process, so we must process events within here to get a "clean" -// shutdown. DebugSession::~DebugSession() { - qCDebug(DEBUGGERGDB); - - if (!stateIsOn(s_dbgNotStarted)) { - stopDebugger(); - // This currently isn't working, so comment out until it can be resolved - at the moment it just causes a delay on stopping kdevelop - //m_process->waitForFinished(); - } - - delete commandQueue_; -} - -void DebugSession::setTesting(bool testing) -{ - m_testing = testing; -} - -KDevelop::IDebugSession::DebuggerState DebugSession::state() const { - return m_sessionState; } -BreakpointController* DebugSession::breakpointController() const +BreakpointController *DebugSession::breakpointController() const { return m_breakpointController; } -IVariableController* DebugSession::variableController() const +VariableController *DebugSession::variableController() const { return m_variableController; } -IFrameStackModel* DebugSession::frameStackModel() const +GdbFrameStackModel *DebugSession::frameStackModel() const { return m_frameStackModel; } -#define ENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) -void DebugSession::setSessionState(DebuggerState state) +GdbDebugger *DebugSession::createDebugger() const { - qCDebug(DEBUGGERGDB) << "STATE CHANGED" << this << state << ENUM_NAME(IDebugSession, DebuggerState, state); - if (state != m_sessionState) { - m_sessionState = state; - emit stateChanged(state); - } + return new GdbDebugger; } -void DebugSession::setupController() +void DebugSession::initializeDebugger() { - // controller -> procLineMaker - connect(this, &DebugSession::ttyStdout, - m_procLineMaker, &ProcessLineMaker::slotReceivedStdout); - connect(this, &DebugSession::ttyStderr, - m_procLineMaker, &ProcessLineMaker::slotReceivedStderr); - -// connect(statusBarIndicator, SIGNAL(doubleClicked()), -// controller, SLOT(explainDebuggerStatus())); - - // TODO: reimplement / re-enable - //connect(this, SIGNAL(addWatchVariable(QString)), controller->variables(), SLOT(slotAddWatchVariable(QString))); - //connect(this, SIGNAL(evaluateExpression(QString)), controller->variables(), SLOT(slotEvaluateExpression(QString))); -} - -void DebugSession::_gdbStateChanged(DBGStateFlags oldState, DBGStateFlags newState) -{ - QString message; - - DebuggerState oldSessionState = state(); - DebuggerState newSessionState = oldSessionState; - DBGStateFlags changedState = oldState ^ newState; - - if (newState & s_dbgNotStarted) { - if (changedState & s_dbgNotStarted) { - message = i18n("Debugger stopped"); - emit finished(); - } - if (oldSessionState != NotStartedState) { - newSessionState = EndedState; - } - } else { - if (newState & s_appNotStarted) { - if (oldSessionState == NotStartedState || oldSessionState == StartingState) { - newSessionState = StartingState; - } else { - newSessionState = StoppedState; - } - } else if (newState & s_programExited) { - if (changedState & s_programExited) { - message = i18n("Process exited"); - } - newSessionState = StoppedState; - } else if (newState & s_appRunning) { - if (changedState & s_appRunning) { - message = i18n("Application is running"); - } - newSessionState = ActiveState; - } else { - if (changedState & s_appRunning) { - message = i18n("Application is paused"); - } - newSessionState = PausedState; - } - } - - // And now? :-) - qCDebug(DEBUGGERGDB) << "state: " << newState << message; - - if (!message.isEmpty()) - emit showMessage(message, 3000); - - emit gdbStateChanged(oldState, newState); - - // must be last, since it can lead to deletion of the DebugSession - if (newSessionState != oldSessionState) { - setSessionState(newSessionState); - } -} - -void DebugSession::examineCoreFile(const QUrl& debugee, const QUrl& coreFile) -{ - if (stateIsOn(s_dbgNotStarted)) - startDebugger(0); - - // TODO support non-local URLs - queueCmd(new GDBCommand(GDBMI::FileExecAndSymbols, debugee.toLocalFile())); - queueCmd(new GDBCommand(GDBMI::NonMI, "core " + coreFile.toLocalFile(), this, &DebugSession::handleCoreFile, CmdHandlesError)); - - raiseEvent(connected_to_program); - raiseEvent(program_state_changed); -} - -void DebugSession::handleCoreFile(const GDBMI::ResultRecord& r) -{ - if (r.reason != "error") { - setStateOn(s_programExited|s_core); - } else { - KMessageBox::information( - qApp->activeWindow(), - i18n("Failed to load core file" - "

Debugger reported the following error:" - "

%1", r["msg"].literal()), - i18n("Debugger error")); - - // How should we proceed at this point? Stop the debugger? - } -} - -void DebugSession::attachToProcess(int pid) -{ - qCDebug(DEBUGGERGDB) << pid; - - if (stateIsOn(s_dbgNotStarted)) - startDebugger(0); - - setStateOn(s_attached); - - //set current state to running, after attaching we will get *stopped response - setStateOn(s_appRunning); - - // Currently, we always start debugger with a name of binary, - // we might be connecting to a different binary completely, - // so cancel all symbol tables gdb has. - // We can't omit application name from gdb invocation - // because for libtool binaries, we have no way to guess - // real binary name. - queueCmd(new GDBCommand(GDBMI::FileExecAndSymbols)); - - queueCmd(new GDBCommand(GDBMI::TargetAttach, QString::number(pid), this, &DebugSession::handleTargetAttach, CmdHandlesError)); - - queueCmd(new SentinelCommand(breakpointController(), &BreakpointController::initSendBreakpoints)); - - raiseEvent(connected_to_program); - - emit raiseFramestackViews(); -} - -void DebugSession::run() -{ - if (stateIsOn(s_appNotStarted|s_dbgNotStarted|s_shuttingDown)) - return; - - queueCmd(new GDBCommand(GDBMI::ExecContinue, QString(), CmdMaybeStartsRunning)); -} - -void DebugSession::stepOut() -{ - if (stateIsOn(s_appNotStarted|s_shuttingDown)) - return; - - queueCmd(new GDBCommand(GDBMI::ExecFinish, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); -} - -void DebugSession::restartDebugger() -{ - // We implement restart as kill + slotRun, as opposed as plain "run" - // command because kill + slotRun allows any special logic in slotRun - // to apply for restart. - // - // That includes: - // - checking for out-of-date project - // - special setup for remote debugging. - // - // Had we used plain 'run' command, restart for remote debugging simply - // would not work. - slotKill(); - run(); -} - -void DebugSession::stopDebugger() -{ - commandQueue_->clear(); - - qCDebug(DEBUGGERGDB) << "DebugSession::slotStopDebugger() called"; - if (stateIsOn(s_shuttingDown) || !m_gdb) - return; - - setStateOn(s_shuttingDown); - qCDebug(DEBUGGERGDB) << "DebugSession::slotStopDebugger() executing"; - - // Get gdb's attention if it's busy. We need gdb to be at the - // command line so we can stop it. - if (!m_gdb.data()->isReady()) - { - qCDebug(DEBUGGERGDB) << "gdb busy on shutdown - interruping"; - m_gdb.data()->interrupt(); - } - - // If the app is attached then we release it here. This doesn't stop - // the app running. - if (stateIsOn(s_attached)) - { - queueCmd(new GDBCommand(GDBMI::TargetDetach)); - emit gdbUserCommandStdout("(gdb) detach\n"); - } - - // Now try to stop gdb running. - queueCmd(new GDBCommand(GDBMI::GdbExit)); - emit gdbUserCommandStdout("(gdb) quit"); - - // We cannot wait forever, kill gdb after 5 seconds if it's not yet quit - QTimer::singleShot(5000, this, SLOT(slotKillGdb())); - - emit reset(); -} - -// Pausing an app removes any pending run commands so that the app doesn't -// start again. If we want to be silent then we remove any pending info -// commands as well. -void DebugSession::interruptDebugger() -{ - Q_ASSERT(m_gdb); - - // Explicitly send the interrupt in case something went wrong with the usual - // ensureGdbListening logic. - m_gdb->interrupt(); - queueCmd(new GDBCommand(GDBMI::ExecInterrupt, QString(), CmdInterrupt)); -} - -void DebugSession::runToCursor() -{ - if (KDevelop::IDocument* doc = KDevelop::ICore::self()->documentController()->activeDocument()) { - KTextEditor::Cursor cursor = doc->cursorPosition(); - if (cursor.isValid()) - runUntil(doc->url(), cursor.line() + 1); - } -} - -void DebugSession::jumpToCursor() -{ - if (KDevelop::IDocument* doc = KDevelop::ICore::self()->documentController()->activeDocument()) { - KTextEditor::Cursor cursor = doc->cursorPosition(); - if (cursor.isValid()) - jumpTo(doc->url(), cursor.line() + 1); - } -} - -void DebugSession::stepOver() -{ - if (stateIsOn(s_appNotStarted|s_shuttingDown)) - return; - - queueCmd(new GDBCommand(GDBMI::ExecNext, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); -} - -void DebugSession::stepOverInstruction() -{ - if (stateIsOn(s_appNotStarted|s_shuttingDown)) - return; - - queueCmd(new GDBCommand(GDBMI::ExecNextInstruction, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); -} - -void DebugSession::stepInto() -{ - if (stateIsOn(s_appNotStarted|s_shuttingDown)) - return; - - queueCmd(new GDBCommand(GDBMI::ExecStep, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); -} - -void DebugSession::stepIntoInstruction() -{ - if (stateIsOn(s_appNotStarted|s_shuttingDown)) - return; - - queueCmd(new GDBCommand(GDBMI::ExecStepInstruction, QString(), CmdMaybeStartsRunning | CmdTemporaryRun)); -} - -void DebugSession::slotDebuggerAbnormalExit() -{ - KMessageBox::information( - KDevelop::ICore::self()->uiController()->activeMainWindow(), - i18n("GDB exited abnormally" - "

This is likely a bug in GDB. " - "Examine the gdb output window and then stop the debugger"), - i18n("GDB exited abnormally")); - - // Note: we don't stop the debugger here, becuse that will hide gdb - // window and prevent the user from finding the exact reason of the - // problem. -} - -bool DebugSession::restartAvaliable() const -{ - if (stateIsOn(s_attached) || stateIsOn(s_core)) { - return false; - } else { - return true; - } -} -void DebugSession::configure() -{ -// KConfigGroup config(KSharedConfig::openConfig(), "GDB Debugger"); -// -// // A a configure.gdb script will prevent these from uncontrolled growth... -// config_configGdbScript_ = config.readEntry("Remote GDB Configure Script", ""); -// config_runShellScript_ = config.readEntry("Remote GDB Shell Script", ""); -// config_runGdbScript_ = config.readEntry("Remote GDB Run Script", ""); -// -// // PORTING TODO: where is this in the ui? -// config_forceBPSet_ = config.readEntry("Allow Forced Breakpoint Set", true); -// -// config_dbgTerminal_ = config.readEntry("Separate Terminal For Application IO", false); -// -// bool old_displayStatic = config_displayStaticMembers_; -// config_displayStaticMembers_ = config.readEntry("Display Static Members",false); -// -// bool old_asmDemangle = config_asmDemangle_; -// config_asmDemangle_ = config.readEntry("Display Demangle Names",true); -// -// bool old_breakOnLoadingLibrary_ = config_breakOnLoadingLibrary_; -// config_breakOnLoadingLibrary_ = config.readEntry("Try Setting Breakpoints On Loading Libraries",true); -// -// // FIXME: should move this into debugger part or variable widget. -// int old_outputRadix = config_outputRadix_; -// #if 0 -// config_outputRadix_ = DomUtil::readIntEntry("Output Radix", 10); -// varTree_->setRadix(config_outputRadix_); -// #endif -// -// -// if (( old_displayStatic != config_displayStaticMembers_ || -// old_asmDemangle != config_asmDemangle_ || -// old_breakOnLoadingLibrary_ != config_breakOnLoadingLibrary_ || -// old_outputRadix != config_outputRadix_) && -// m_gdb) -// { -// bool restart = false; -// if (stateIsOn(s_dbgBusy)) -// { -// slotPauseApp(); -// restart = true; -// } -// -// if (old_displayStatic != config_displayStaticMembers_) -// { -// if (config_displayStaticMembers_) -// queueCmd(new GDBCommand(GDBMI::GdbSet, "print static-members on")); -// else -// queueCmd(new GDBCommand(GDBMI::GdbSet, "print static-members off")); -// } -// if (old_asmDemangle != config_asmDemangle_) -// { -// if (config_asmDemangle_) -// queueCmd(new GDBCommand(GDBMI::GdbSet, "print asm-demangle on")); -// else -// queueCmd(new GDBCommand(GDBMI::GdbSet, "print asm-demangle off")); -// } -// -// // Disabled for MI port. -// if (old_outputRadix != config_outputRadix_) -// { -// queueCmd(new GDBCommand(GDBMI::GdbSet, QString().sprintf("output-radix %d", -// config_outputRadix_))); -// -// // FIXME: should do this in variable widget anyway. -// // After changing output radix, need to refresh variables view. -// raiseEvent(program_state_changed); -// -// } -// -// if (config_configGdbScript_.isValid()) -// queueCmd(new GDBCommand(GDBMI::NonMI, "source " + config_configGdbScript_.toLocalFile())); -// -// -// if (restart) -// queueCmd(new GDBCommand(GDBMI::ExecContinue)); -// } -} - -// ************************************************************************** - -void DebugSession::addCommand(GDBCommand* cmd) -{ - queueCmd(cmd); -} - -void DebugSession::addCommand(GDBMI::CommandType type, const QString& str) -{ - queueCmd(new GDBCommand(type, str)); -} - -// Fairly obvious that we'll add whatever command you give me to a queue -// Not quite so obvious though is that if we are going to run again. then any -// information requests become redundent and must be removed. -// We also try and run whatever command happens to be at the head of -// the queue. -void DebugSession::queueCmd(GDBCommand *cmd) -{ - if (stateIsOn(s_dbgNotStarted)) - { - KMessageBox::information( - qApp->activeWindow(), - i18n("Gdb command sent when debugger is not running
" - "The command was:
%1", cmd->initialString()), - i18n("Internal error")); - return; - } - - if (stateReloadInProgress_) - cmd->setStateReloading(true); - - commandQueue_->enqueue(cmd); - - qCDebug(DEBUGGERGDB) << "QUEUE: " << cmd->initialString() << (stateReloadInProgress_ ? "(state reloading)" : ""); - - bool varCommandWithContext= (cmd->type() >= GDBMI::VarAssign - && cmd->type() <= GDBMI::VarUpdate - && cmd->type() != GDBMI::VarDelete); - - bool stackCommandWithContext = (cmd->type() >= GDBMI::StackInfoDepth - && cmd->type() <= GDBMI::StackListLocals); - - if (varCommandWithContext || stackCommandWithContext) - { - if (cmd->thread() == -1) - qCDebug(DEBUGGERGDB) << "\t--thread will be added on execution"; - - if (cmd->frame() == -1) - qCDebug(DEBUGGERGDB) << "\t--frame will be added on execution"; - } - - setStateOn(s_dbgBusy); - raiseEvent(debugger_busy); - - executeCmd(); -} - -void DebugSession::executeCmd() -{ - Q_ASSERT(m_gdb); - - if (stateIsOn(s_dbgNotListening) && commandQueue_->haveImmediateCommand()) { - // We may have to call this even while a command is currently executing, because - // Gdb can get into a state where a command such as ExecRun does not send a response - // while the inferior is running. - ensureGdbListening(); - } - - if (!m_gdb.data()->isReady()) - return; - - GDBCommand* currentCmd = commandQueue_->nextCommand(); - if (!currentCmd) - return; - - if (currentCmd->flags() & (CmdMaybeStartsRunning | CmdInterrupt)) { - setStateOff(s_automaticContinue); - } - - if (currentCmd->flags() & CmdMaybeStartsRunning) { - // GDB can be in a state where it is listening for commands while the program is running. - // However, when we send a command such as ExecContinue in this state, GDB may return to - // the non-listening state without acknowledging that the ExecContinue command has even - // finished, let alone sending a new notification about the program's running state. - // So let's be extra cautious about ensuring that we will wake GDB up again if required. - setStateOn(s_dbgNotListening); - } - - bool varCommandWithContext= (currentCmd->type() >= GDBMI::VarAssign - && currentCmd->type() <= GDBMI::VarUpdate - && currentCmd->type() != GDBMI::VarDelete); - - bool stackCommandWithContext = (currentCmd->type() >= GDBMI::StackInfoDepth - && currentCmd->type() <= GDBMI::StackListLocals); - - if (varCommandWithContext || stackCommandWithContext) - { - // Most var commands should be executed in the context - // of the selected thread and frame. - if (currentCmd->thread() == -1) - currentCmd->setThread(frameStackModel()->currentThread()); - - if (currentCmd->frame() == -1) - currentCmd->setFrame(frameStackModel()->currentFrame()); - } - - QString commandText = currentCmd->cmdToSend(); - bool bad_command = false; - QString message; - - int length = commandText.length(); - // No i18n for message since it's mainly for debugging. - if (length == 0) - { - // The command might decide it's no longer necessary to send - // it. - if (SentinelCommand* sc = dynamic_cast(currentCmd)) - { - qCDebug(DEBUGGERGDB) << "SEND: sentinel command, not sending"; - sc->invokeHandler(); - } - else - { - qCDebug(DEBUGGERGDB) << "SEND: command " << currentCmd->initialString() - << "changed its mind, not sending"; - } - - delete currentCmd; - executeCmd(); - return; - } - else - { - if (commandText[length-1] != '\n') - { - bad_command = true; - message = "Debugger command does not end with newline"; - } - } - if (bad_command) - { - KMessageBox::information(qApp->activeWindow(), - i18n("Invalid debugger command
%1", message), - i18n("Invalid debugger command")); - executeCmd(); - return; - } - - m_gdb.data()->execute(currentCmd); -} - -// ************************************************************************** - -void DebugSession::destroyCmds() -{ - commandQueue_->clear(); -} - - -void DebugSession::slotProgramStopped(const GDBMI::AsyncRecord& r) -{ - /* By default, reload all state on program stop. */ - state_reload_needed = true; - setStateOff(s_appRunning); - setStateOff(s_dbgNotListening); - - QString reason; - if (r.hasField("reason")) reason = r["reason"].literal(); - - if (reason == "exited-normally" || reason == "exited") - { - if (r.hasField("exit-code")) { - programNoApp(i18n("Exited with return code: %1", r["exit-code"].literal())); - } else { - programNoApp(i18n("Exited normally")); - } - state_reload_needed = false; - return; - } - - if (reason == "exited-signalled") - { - programNoApp(i18n("Exited on signal %1", r["signal-name"].literal())); - state_reload_needed = false; - return; - } - - if (reason == "watchpoint-scope") - { - QString number = r["wpnum"].literal(); - - // FIXME: shuld remove this watchpoint - // But first, we should consider if removing all - // watchpoinst on program exit is the right thing to - // do. - - queueCmd(new GDBCommand(GDBMI::ExecContinue, QString(), CmdMaybeStartsRunning)); - - state_reload_needed = false; - return; - } - - bool wasInterrupt = false; - - if (reason == "signal-received") - { - QString name = r["signal-name"].literal(); - QString user_name = r["signal-meaning"].literal(); - - // SIGINT is a "break into running program". - // We do this when the user set/mod/clears a breakpoint but the - // application is running. - // And the user does this to stop the program also. - if (name == "SIGINT" && stateIsOn(s_interruptSent)) { - wasInterrupt = true; - } else { - // Whenever we have a signal raised then tell the user, but don't - // end the program as we want to allow the user to look at why the - // program has a signal that's caused the prog to stop. - // Continuing from SIG FPE/SEGV will cause a "Cannot ..." and - // that'll end the program. - programFinished(i18n("Program received signal %1 (%2)", name, user_name)); - - m_hasCrashed = true; - } - } - - if (!reason.contains("exited")) - { - // FIXME: we should immediately update the current thread and - // frame in the framestackmodel, so that any user actions - // are in that thread. However, the way current framestack model - // is implemented, we can't change thread id until we refresh - // the entire list of threads -- otherwise we might set a thread - // id that is not already in the list, and it will be upset. - - //Indicates if program state should be reloaded immediately. - bool updateState = false; - - if (r.hasField("frame")) { - const GDBMI::Value& frame = r["frame"]; - QString file, line, addr; - - if (frame.hasField("fullname")) file = frame["fullname"].literal();; - if (frame.hasField("line")) line = frame["line"].literal(); - if (frame.hasField("addr")) addr = frame["addr"].literal(); - - // gdb counts lines from 1 and we don't - setCurrentPosition(QUrl::fromLocalFile(file), line.toInt() - 1, addr); - - updateState = true; - } - - if (updateState) { - reloadProgramState(); - } - } - - setStateOff(s_interruptSent); - if (!wasInterrupt) - setStateOff(s_automaticContinue); -} - -bool DebugSession::hasCrashed() const -{ - return m_hasCrashed; -} - -void DebugSession::processNotification(const GDBMI::AsyncRecord & async) -{ - if (async.reason == "thread-group-started") { - setStateOff(s_appNotStarted | s_programExited); - } else if (async.reason == "thread-group-exited") { - setStateOn(s_programExited); - } else if (async.reason == "library-loaded") { - // do nothing - } else if (async.reason == "breakpoint-created") { - breakpointController()->notifyBreakpointCreated(async); - } else if (async.reason == "breakpoint-modified") { - breakpointController()->notifyBreakpointModified(async); - } else if (async.reason == "breakpoint-deleted") { - breakpointController()->notifyBreakpointDeleted(async); - } else { - qCDebug(DEBUGGERGDB) << "Unhandled notification: " << async.reason; - } -} - -void DebugSession::reloadProgramState() -{ - raiseEvent(program_state_changed); - state_reload_needed = false; -} - - -// ************************************************************************** - -// There is no app anymore. This can be caused by program exiting -// an invalid program specified or ... -// gdb is still running though, but only the run command (may) make sense -// all other commands are disabled. -void DebugSession::programNoApp(const QString& msg) -{ - qCDebug(DEBUGGERGDB) << msg; - - setState(s_appNotStarted|s_programExited|(state_&s_shuttingDown)); - - destroyCmds(); - - // The application has existed, but it's possible that - // some of application output is still in the pipe. We use - // different pipes to communicate with gdb and to get application - // output, so "exited" message from gdb might have arrived before - // last application output. Get this last bit. - - // Note: this method can be called when we open an invalid - // core file. In that case, tty_ won't be set. - if (m_tty){ - m_tty->readRemaining(); - // Tty is no longer usable, delete it. Without this, QSocketNotifier - // will continiously bomd STTY with signals, so we need to either disable - // QSocketNotifier, or delete STTY. The latter is simpler, since we can't - // reuse it for future debug sessions anyway. - m_tty.reset(0); - } - - stopDebugger(); - - raiseEvent(program_exited); - raiseEvent(debugger_exited); - - emit showMessage(msg, 0); - - programFinished(msg); -} - - -void DebugSession::programFinished(const QString& msg) -{ - QString m = QString("*** %0 ***").arg(msg.trimmed()); - emit applicationStandardErrorLines(QStringList(m)); - - /* Also show message in gdb window, so that users who - prefer to look at gdb window know what's up. */ - emit gdbUserCommandStdout(m); -} - - -bool DebugSession::startDebugger(KDevelop::ILaunchConfiguration* cfg) -{ - qCDebug(DEBUGGERGDB) << "Starting debugger controller"; - - if(m_gdb) { - qWarning() << "m_gdb object still existed"; - delete m_gdb.data(); - m_gdb.clear(); - } - - GDB* gdb = new GDB(this); - m_gdb = gdb; - - // FIXME: here, we should wait until GDB is up and waiting for input. - // Then, clear s_dbgNotStarted - // It's better to do this right away so that the state bit is always - // correct. - - connect(gdb, &GDB::applicationOutput, - this, [this](const QString& output) { - emit applicationStandardOutputLines(output.split(QRegularExpression("[\r\n]"),QString::SkipEmptyParts)); - }); - connect(gdb, &GDB::userCommandOutput, this, - &DebugSession::gdbUserCommandStdout); - connect(gdb, &GDB::internalCommandOutput, this, - &DebugSession::gdbInternalCommandStdout); - - connect(gdb, &GDB::ready, this, &DebugSession::gdbReady); - connect(gdb, &GDB::gdbExited, this, &DebugSession::gdbExited); - connect(gdb, &GDB::programStopped, - this, &DebugSession::slotProgramStopped); - connect(gdb, &GDB::programStopped, - this, &DebugSession::programStopped); - connect(gdb, &GDB::programRunning, - this, &DebugSession::programRunning); - connect(gdb, &GDB::notification, - this, &DebugSession::processNotification); - - // Start gdb. Do this after connecting all signals so that initial - // GDB output, and important events like "GDB died" are reported. - - { - QStringList extraArguments; - if (m_testing) - extraArguments << "--nx"; // do not load any .gdbinit files - - if (cfg) - { - KConfigGroup config = cfg->config(); - m_gdb.data()->start(config, extraArguments); - } - else - { - // FIXME: this is hack, I am not sure there's any way presently - // to edit this via GUI. - KConfigGroup config(KSharedConfig::openConfig(), "GDB Debugger"); - m_gdb.data()->start(config, extraArguments); - } - } - - setStateOff(s_dbgNotStarted); - - // Initialise gdb. At this stage gdb is sitting wondering what to do, - // and to whom. Organise a few things, then set up the tty for the application, - // and the application itself //queueCmd(new GDBCommand(GDBMI::EnableTimings, "yes")); - queueCmd(new CliCommand(GDBMI::GdbShow, "version", this, &DebugSession::handleVersion)); + queueCmd(new CliCommand(MI::GdbShow, "version", this, &DebugSession::handleVersion)); // This makes gdb pump a variable out on one line. - queueCmd(new GDBCommand(GDBMI::GdbSet, "width 0")); - queueCmd(new GDBCommand(GDBMI::GdbSet, "height 0")); + queueCmd(new MICommand(MI::GdbSet, "width 0")); + queueCmd(new MICommand(MI::GdbSet, "height 0")); - queueCmd(new GDBCommand(GDBMI::SignalHandle, "SIG32 pass nostop noprint")); - queueCmd(new GDBCommand(GDBMI::SignalHandle, "SIG41 pass nostop noprint")); - queueCmd(new GDBCommand(GDBMI::SignalHandle, "SIG42 pass nostop noprint")); - queueCmd(new GDBCommand(GDBMI::SignalHandle, "SIG43 pass nostop noprint")); + queueCmd(new MICommand(MI::SignalHandle, "SIG32 pass nostop noprint")); + queueCmd(new MICommand(MI::SignalHandle, "SIG41 pass nostop noprint")); + queueCmd(new MICommand(MI::SignalHandle, "SIG42 pass nostop noprint")); + queueCmd(new MICommand(MI::SignalHandle, "SIG43 pass nostop noprint")); - queueCmd(new GDBCommand(GDBMI::EnablePrettyPrinting)); + queueCmd(new MICommand(MI::EnablePrettyPrinting)); - queueCmd(new GDBCommand(GDBMI::GdbSet, "charset UTF-8")); - queueCmd(new GDBCommand(GDBMI::GdbSet, "print sevenbit-strings off")); + queueCmd(new MICommand(MI::GdbSet, "charset UTF-8")); + queueCmd(new MICommand(MI::GdbSet, "print sevenbit-strings off")); - QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdevgdb/printers/gdbinit"); + QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, + "kdevgdb/printers/gdbinit"); if (!fileName.isEmpty()) { QFileInfo fileInfo(fileName); - QString quotedPrintersPath = fileInfo.dir().path().replace('\\', "\\\\").replace('"', "\\\""); - queueCmd(new GDBCommand(GDBMI::NonMI, + QString quotedPrintersPath = fileInfo.dir().path() + .replace('\\', "\\\\") + .replace('"', "\\\""); + queueCmd(new MICommand(MI::NonMI, QString("python sys.path.insert(0, \"%0\")").arg(quotedPrintersPath))); - queueCmd(new GDBCommand(GDBMI::NonMI, "source " + fileName)); + queueCmd(new MICommand(MI::NonMI, "source " + fileName)); } - return true; + qCDebug(DEBUGGERGDB) << "Initialized GDB"; } -bool DebugSession::startProgram(KDevelop::ILaunchConfiguration* cfg, IExecutePlugin* iface) +void DebugSession::configure(ILaunchConfiguration *cfg) { - if (stateIsOn( s_appNotStarted ) ) - { - emit showMessage(i18n("Running program"), 1000); - } - - if (stateIsOn(s_dbgNotStarted)) { - if (!startDebugger(cfg)) - return false; - } - - if (stateIsOn(s_shuttingDown)) { - qCDebug(DEBUGGERGDB) << "Tried to run when debugger shutting down"; - return false; - } - - - + // Read Configuration values KConfigGroup grp = cfg->config(); - KDevelop::EnvironmentGroupList l(KSharedConfig::openConfig()); + bool breakOnStart = grp.readEntry(KDevMI::breakOnStartEntry, false); + bool displayStaticMembers = grp.readEntry(KDevMI::staticMembersEntry, false); + bool asmDemangle = grp.readEntry(KDevMI::demangleNamesEntry, true); - QString envgrp = iface->environmentGroup( cfg ); - if( envgrp.isEmpty() ) - { - qWarning() << i18n("No environment group specified, looks like a broken " - "configuration, please check run configuration '%1'. " - "Using default environment group.", cfg->name() ); - envgrp = l.defaultGroup(); - } - - - if (grp.readEntry("Break on Start", false)) { - BreakpointModel* m = KDevelop::ICore::self()->debugController()->breakpointModel(); + if (breakOnStart) { + BreakpointModel* m = ICore::self()->debugController()->breakpointModel(); bool found = false; - foreach (KDevelop::Breakpoint *b, m->breakpoints()) { + foreach (Breakpoint *b, m->breakpoints()) { if (b->location() == "main") { found = true; break; } } if (!found) { - KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint("main"); + m->addCodeBreakpoint("main"); } } + // Needed so that breakpoint widget has a chance to insert breakpoints. + // FIXME: a bit hacky, as we're really not ready for new commands. + setDebuggerStateOn(s_dbgBusy); + raiseEvent(debugger_ready); - - // Configuration values - bool config_displayStaticMembers_ = grp.readEntry( GDBDebugger::staticMembersEntry, false ); - bool config_asmDemangle_ = grp.readEntry( GDBDebugger::demangleNamesEntry, true ); - QUrl config_configGdbScript_ = grp.readEntry( GDBDebugger::remoteGdbConfigEntry, QUrl() ); - QUrl config_runShellScript_ = grp.readEntry( GDBDebugger::remoteGdbShellEntry, QUrl() ); - QUrl config_runGdbScript_ = grp.readEntry( GDBDebugger::remoteGdbRunEntry, QUrl() ); - - Q_ASSERT(iface); - bool config_useExternalTerminal = iface->useTerminal( cfg ); - QString config_externalTerminal = iface->terminal( cfg ); - if (!config_externalTerminal.isEmpty()) { - // the external terminal cmd contains additional arguments, just get the terminal name - config_externalTerminal = KShell::splitArgs(config_externalTerminal).first(); + if (displayStaticMembers) { + queueCmd(new MICommand(MI::GdbSet, "print static-members on")); + } else { + queueCmd(new MICommand(MI::GdbSet, "print static-members off")); } - m_tty.reset(new STTY(config_useExternalTerminal, config_externalTerminal)); - if (!config_useExternalTerminal) - { - connect( m_tty.data(), &STTY::OutOutput, this, &DebugSession::ttyStdout ); - connect( m_tty.data(), &STTY::ErrOutput, this, &DebugSession::ttyStderr ); + if (asmDemangle) { + queueCmd(new MICommand(MI::GdbSet, "print asm-demangle on")); + } else { + queueCmd(new MICommand(MI::GdbSet, "print asm-demangle off")); } - QString tty(m_tty->getSlave()); - if (tty.isEmpty()) - { - KMessageBox::information(qApp->activeWindow(), m_tty->lastError(), i18n("Warning")); + qCDebug(DEBUGGERGDB) << "Per inferior configuration done"; +} - m_tty.reset(0); - return false; - } +bool DebugSession::execInferior(ILaunchConfiguration *cfg, const QString &executable) +{ + qCDebug(DEBUGGERGDB) << "Executing inferior"; - queueCmd(new GDBCommand(GDBMI::InferiorTtySet, tty)); + // debugger specific config + configure(cfg); - // Only dummy err here, actual erros have been checked already in the job and we don't get here if there were any - QString err; - QString executable = iface->executable(cfg, err).toLocalFile(); + KConfigGroup grp = cfg->config(); + QUrl configGdbScript = grp.readEntry(KDevMI::remoteGdbConfigEntry, QUrl()); + QUrl runShellScript = grp.readEntry(KDevMI::remoteGdbShellEntry, QUrl()); + QUrl runGdbScript = grp.readEntry(KDevMI::remoteGdbRunEntry, QUrl()); - QStringList arguments = iface->arguments(cfg, err); - // Change the "Working directory" to the correct one - QString dir = iface->workingDirectory(cfg).toLocalFile(); - if (dir.isEmpty()) { - dir = QFileInfo(executable).absolutePath(); + // handle remote debug + if (configGdbScript.isValid()) { + queueCmd(new MICommand(MI::NonMI, "source " + KShell::quoteArg(configGdbScript.toLocalFile()))); } - queueCmd(new GDBCommand(GDBMI::EnvironmentCd, '"' + dir + '"')); - - // Set the run arguments - if (!arguments.isEmpty()) - queueCmd( - new GDBCommand(GDBMI::ExecArguments, KShell::joinArgs( arguments ))); - - foreach (const QString& envvar, l.createEnvironment(envgrp, QStringList())) - queueCmd(new GDBCommand(GDBMI::GdbSet, "environment " + envvar)); - - // Needed so that breakpoint widget has a chance to insert breakpoints. - // FIXME: a bit hacky, as we're really not ready for new commands. - setStateOn(s_dbgBusy); - raiseEvent(debugger_ready); - - - if (config_displayStaticMembers_) - queueCmd(new GDBCommand(GDBMI::GdbSet, "print static-members on")); - else - queueCmd(new GDBCommand(GDBMI::GdbSet, "print static-members off")); - if (config_asmDemangle_) - queueCmd(new GDBCommand(GDBMI::GdbSet, "print asm-demangle on")); - else - queueCmd(new GDBCommand(GDBMI::GdbSet, "print asm-demangle off")); - - if (config_configGdbScript_.isValid()) - queueCmd(new GDBCommand(GDBMI::NonMI, "source " + KShell::quoteArg(config_configGdbScript_.toLocalFile()))); - - - if (!config_runShellScript_.isEmpty()) { - // Special for remote debug... + // FIXME: have a check box option that controls remote debugging + if (runShellScript.isValid()) { + // Special for remote debug, the remote inferior is started by this shell script QByteArray tty(m_tty->getSlave().toLatin1()); QByteArray options = QByteArray(">") + tty + QByteArray(" 2>&1 <") + tty; QProcess *proc = new QProcess; QStringList arguments; - arguments << "-c" << KShell::quoteArg(config_runShellScript_.toLocalFile()) + - ' ' + KShell::quoteArg(executable) + QString::fromLatin1( options ); + arguments << "-c" << KShell::quoteArg(runShellScript.toLocalFile()) + + ' ' + KShell::quoteArg(executable) + QString::fromLatin1(options); qCDebug(DEBUGGERGDB) << "starting sh" << arguments; proc->start("sh", arguments); //PORTING TODO QProcess::DontCare); } - if (!config_runGdbScript_.isEmpty()) {// gdb script at run is requested + if (runGdbScript.isValid()) { + // Special for remote debug, gdb script at run is requested, to connect to remote inferior // Race notice: wait for the remote gdbserver/executable // - but that might be an issue for this script to handle... + // Note: script could contain "run" or "continue" + // Future: the shell script should be able to pass info (like pid) // to the gdb script... - queueCmd(new SentinelCommand([this, config_runGdbScript_]() { + queueCmd(new SentinelCommand([this, runGdbScript]() { breakpointController()->initSendBreakpoints(); breakpointController()->setDeleteDuplicateBreakpoints(true); - qCDebug(DEBUGGERGDB) << "Running gdb script " << KShell::quoteArg(config_runGdbScript_.toLocalFile()); - queueCmd(new GDBCommand(GDBMI::NonMI, "source " + KShell::quoteArg(config_runGdbScript_.toLocalFile()), - [this](const GDBMI::ResultRecord&) {breakpointController()->setDeleteDuplicateBreakpoints(false);}, + qCDebug(DEBUGGERGDB) << "Running gdb script " << KShell::quoteArg(runGdbScript.toLocalFile()); + + queueCmd(new MICommand(MI::NonMI, "source " + KShell::quoteArg(runGdbScript.toLocalFile()), + [this](const MI::ResultRecord&) { + breakpointController()->setDeleteDuplicateBreakpoints(false); + }, CmdMaybeStartsRunning)); raiseEvent(connected_to_program); - // Note: script could contain "run" or "continue" }, CmdMaybeStartsRunning)); - } - else - { - queueCmd(new GDBCommand(GDBMI::FileExecAndSymbols, KShell::quoteArg(executable), this, &DebugSession::handleFileExecAndSymbols, CmdHandlesError)); + } else { + // normal local debugging + queueCmd(new MICommand(MI::FileExecAndSymbols, KShell::quoteArg(executable), + this, &DebugSession::handleFileExecAndSymbols, + CmdHandlesError)); raiseEvent(connected_to_program); queueCmd(new SentinelCommand([this]() { breakpointController()->initSendBreakpoints(); - queueCmd(new GDBCommand(GDBMI::ExecRun, QString(), CmdMaybeStartsRunning)); + queueCmd(new MICommand(MI::ExecRun, QString(), CmdMaybeStartsRunning)); }, CmdMaybeStartsRunning)); } - - { - QString startWith = grp.readEntry(GDBDebugger::startWithEntry, QString("ApplicationOutput")); - if (startWith == "GdbConsole") { - emit raiseGdbConsoleViews(); - } else if (startWith == "FrameStack") { - emit raiseFramestackViews(); - } else { - //ApplicationOutput is raised in DebugJob (by setting job to Verbose/Silent) - } - } - return true; } - -// ************************************************************************** -// SLOTS -// ***** -// For most of these slots data can only be sent to gdb when it -// isn't busy and it is running. - -// ************************************************************************** - -void DebugSession::slotKillGdb() -{ - if (!stateIsOn(s_programExited) && stateIsOn(s_shuttingDown)) - { - qCDebug(DEBUGGERGDB) << "gdb not shutdown - killing"; - m_gdb.data()->kill(); - - setState(s_dbgNotStarted | s_appNotStarted); - - raiseEvent(debugger_exited); - } -} - -// ************************************************************************** - -void DebugSession::slotKill() -{ - if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) - return; - - if (stateIsOn(s_dbgBusy)) - { - interruptDebugger(); - } - - // The -exec-abort is not implemented in gdb - // queueCmd(new GDBCommand(GDBMI::ExecAbort)); - queueCmd(new GDBCommand(GDBMI::NonMI, "kill")); -} - -// ************************************************************************** - -void DebugSession::runUntil(const QUrl& url, int line) -{ - if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) - return; - - if (!url.isValid()) - queueCmd(new GDBCommand(GDBMI::ExecUntil, QString::number(line), - CmdMaybeStartsRunning | CmdTemporaryRun)); - else - queueCmd(new GDBCommand(GDBMI::ExecUntil, - QString("%1:%2").arg(url.toLocalFile()).arg(line), - CmdMaybeStartsRunning | CmdTemporaryRun)); -} - -void DebugSession::runUntil(const QString& address) -{ - if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) - return; - - if (!address.isEmpty()) { - queueCmd(new GDBCommand(GDBMI::ExecUntil, QString("*%1").arg(address), - CmdMaybeStartsRunning | CmdTemporaryRun)); - } -} -// ************************************************************************** - -void DebugSession::jumpToMemoryAddress(const QString& address) -{ - if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) - return; - - if (!address.isEmpty()) { - queueCmd(new GDBCommand(GDBMI::NonMI, QString("tbreak *%1").arg(address))); - queueCmd(new GDBCommand(GDBMI::NonMI, QString("jump *%1").arg(address))); - } -} - -void DebugSession::jumpTo(const QUrl& url, int line) -{ - if (stateIsOn(s_dbgNotStarted|s_shuttingDown)) - return; - - if (url.isValid()) { - queueCmd(new GDBCommand(GDBMI::NonMI, QString("tbreak %1:%2").arg(url.toLocalFile()).arg(line))); - queueCmd(new GDBCommand(GDBMI::NonMI, QString("jump %1:%2").arg(url.toLocalFile()).arg(line))); - } -} - -// ************************************************************************** - -// FIXME: connect to GDB's slot. -void DebugSession::defaultErrorHandler(const GDBMI::ResultRecord& result) -{ - QString msg = result["msg"].literal(); - - if (msg.contains("No such process")) - { - setState(s_appNotStarted|s_programExited); - raiseEvent(program_exited); - return; - } - - KMessageBox::information( - qApp->activeWindow(), - i18n("Debugger error" - "

Debugger reported the following error:" - "

%1", result["msg"].literal()), - i18n("Debugger error")); - - // Error most likely means that some change made in GUI - // was not communicated to the gdb, so GUI is now not - // in sync with gdb. Resync it. - // - // Another approach is to make each widget reload it content - // on errors from commands that it sent, but that's too complex. - // Errors are supposed to happen rarely, so full reload on error - // is not a big deal. Well, maybe except for memory view, but - // it's no auto-reloaded anyway. - // - // Also, don't reload state on errors appeared during state - // reloading! - if (!m_gdb.data()->currentCommand()->stateReloading()) - raiseEvent(program_state_changed); -} - -void DebugSession::gdbReady() -{ - stateReloadInProgress_ = false; - - executeCmd(); - if (m_gdb->isReady()) - { - /* There is nothing in the command queue and no command is currently executing. */ - - if (stateIsOn(s_automaticContinue)) { - if (!stateIsOn(s_appRunning)) { - qCDebug(DEBUGGERGDB) << "Posting automatic continue"; - queueCmd(new GDBCommand(GDBMI::ExecContinue, QString(), CmdMaybeStartsRunning)); - } - setStateOff(s_automaticContinue); - return; - } - - if (state_reload_needed && !stateIsOn(s_appRunning)) - { - qCDebug(DEBUGGERGDB) << "Finishing program stop"; - // Set to false right now, so that if 'actOnProgramPauseMI_part2' - // sends some commands, we won't call it again when handling replies - // from that commands. - state_reload_needed = false; - reloadProgramState(); - } - - qCDebug(DEBUGGERGDB) << "No more commands"; - setStateOff(s_dbgBusy); - raiseEvent(debugger_ready); - } -} - -void DebugSession::gdbExited() -{ - qCDebug(DEBUGGERGDB); - /* Technically speaking, GDB is likely not to kill the application, and - we should have some backup mechanism to make sure the application is - killed by KDevelop. But even if application stays around, we no longer - can control it in any way, so mark it as exited. */ - setStateOn(s_appNotStarted); - setStateOn(s_dbgNotStarted); - setStateOff(s_shuttingDown); -} - -void DebugSession::ensureGdbListening() -{ - Q_ASSERT(m_gdb); - m_gdb->interrupt(); - setStateOn(s_interruptSent); - if (stateIsOn(s_appRunning)) - setStateOn(s_automaticContinue); - setStateOff(s_dbgNotListening); -} - -// FIXME: I don't fully remember what is the business with -// stateReloadInProgress_ and whether we can lift it to the -// generic level. -void DebugSession::raiseEvent(event_t e) -{ - if (e == program_exited || e == debugger_exited) - { - stateReloadInProgress_ = false; - } - - if (e == program_state_changed) - { - stateReloadInProgress_ = true; - qCDebug(DEBUGGERGDB) << "State reload in progress\n"; - } - - IDebugSession::raiseEvent(e); - - if (e == program_state_changed) - { - stateReloadInProgress_ = false; - } -} - -// ************************************************************************** - -void DebugSession::slotUserGDBCmd(const QString& cmd) -{ - queueCmd(new UserCommand(GDBMI::NonMI, cmd)); - - // User command can theoreticall modify absolutely everything, - // so need to force a reload. - - // We can do it right now, and don't wait for user command to finish - // since commands used to reload all view will be executed after - // user command anyway. - if (!stateIsOn(s_appNotStarted) && !stateIsOn(s_programExited)) - raiseEvent(program_state_changed); -} - -void DebugSession::explainDebuggerStatus() -{ - GDBCommand* currentCmd_ = m_gdb.data()->currentCommand(); - QString information = - i18np("1 command in queue\n", "%1 commands in queue\n", commandQueue_->count()) + - i18ncp("Only the 0 and 1 cases need to be translated", "1 command being processed by gdb\n", "%1 commands being processed by gdb\n", (currentCmd_ ? 1 : 0)) + - i18n("Debugger state: %1\n", state_); - - if (currentCmd_) - { - QString extra = i18n("Current command class: '%1'\n" - "Current command text: '%2'\n" - "Current command original text: '%3'\n", - typeid(*currentCmd_).name(), - currentCmd_->cmdToSend(), - currentCmd_->initialString()); - - information += extra; - } - - KMessageBox::information(qApp->activeWindow(), information, - i18n("Debugger status")); -} - -bool DebugSession::stateIsOn(DBGStateFlags state) const -{ - return state_ & state; -} - -DBGStateFlags DebugSession::debuggerState() const -{ - return state_; -} - -void DebugSession::setStateOn(DBGStateFlags stateOn) -{ - DBGStateFlags oldState = state_; - - debugStateChange(state_, state_ | stateOn); - state_ |= stateOn; - - _gdbStateChanged(oldState, state_); -} - -void DebugSession::setStateOff(DBGStateFlags stateOff) -{ - DBGStateFlags oldState = state_; - - debugStateChange(state_, state_ & ~stateOff); - state_ &= ~stateOff; - - _gdbStateChanged(oldState, state_); -} - -void DebugSession::setState(DBGStateFlags newState) -{ - DBGStateFlags oldState = state_; - - debugStateChange(state_, newState); - state_ = newState; - - _gdbStateChanged(oldState, state_); -} - -void DebugSession::debugStateChange(DBGStateFlags oldState, DBGStateFlags newState) -{ - int delta = oldState ^ newState; - if (delta) - { - QString out = "STATE:"; -#define STATE_CHECK(name) \ - do { \ - if (delta & name) { \ - out += ((newState & name) ? " +" : " -"); \ - out += #name; \ - delta &= ~name; \ - } \ - } while (0) - STATE_CHECK(s_dbgNotStarted); - STATE_CHECK(s_appNotStarted); - STATE_CHECK(s_programExited); - STATE_CHECK(s_attached); - STATE_CHECK(s_core); - STATE_CHECK(s_shuttingDown); - STATE_CHECK(s_dbgBusy); - STATE_CHECK(s_appRunning); - STATE_CHECK(s_dbgNotListening); - STATE_CHECK(s_automaticContinue); -#undef STATE_CHECK - - for (unsigned int i = 0; delta != 0 && i < 32; ++i) { - if (delta & (1 << i)) { - delta &= ~(1 << i); - out += ((1 << i) & newState) ? " +" : " -"; - out += QString::number(i); - } - } - qCDebug(DEBUGGERGDB) << out; - } -} - -void DebugSession::programRunning() -{ - setStateOn(s_appRunning); - raiseEvent(program_running); - - if (commandQueue_->haveImmediateCommand() || - (m_gdb->currentCommand() && (m_gdb->currentCommand()->flags() & (CmdImmediately | CmdInterrupt)))) { - ensureGdbListening(); - } else { - setStateOn(s_dbgNotListening); - } -} - void DebugSession::handleVersion(const QStringList& s) { qCDebug(DEBUGGERGDB) << s.first(); @@ -1481,6 +248,7 @@ //for unittest qFatal("You need a graphical application."); } + KMessageBox::error( qApp->activeWindow(), i18n("You need gdb 7.0.0 or higher.
" @@ -1490,8 +258,7 @@ } } - -void DebugSession::handleFileExecAndSymbols(const GDBMI::ResultRecord& r) +void DebugSession::handleFileExecAndSymbols(const MI::ResultRecord& r) { if (r.reason == "error") { KMessageBox::error( @@ -1502,19 +269,3 @@ stopDebugger(); } } - -void DebugSession::handleTargetAttach(const GDBMI::ResultRecord& r) -{ - if (r.reason == "error") { - KMessageBox::error( - qApp->activeWindow(), - i18n("Could not attach debugger:
")+ - r["msg"].literal(), - i18n("Startup error")); - stopDebugger(); - } -} - -} - - diff --git a/debuggers/gdb/disassemblewidget.h b/debuggers/gdb/disassemblewidget.h --- a/debuggers/gdb/disassemblewidget.h +++ b/debuggers/gdb/disassemblewidget.h @@ -24,10 +24,9 @@ #ifndef _DISASSEMBLEWIDGET_H_ #define _DISASSEMBLEWIDGET_H_ -#include "mi/gdbmi.h" +#include "mi/mi.h" #include - #include #include @@ -44,8 +43,7 @@ class IDebugSession; } -namespace GDBDebugger -{ +namespace KDevMI { namespace GDB { class RegistersManager; @@ -136,8 +134,8 @@ const QString& to=QString() ); /// callbacks for GDBCommands - void disassembleMemoryHandler(const GDBMI::ResultRecord& r); - void updateExecutionAddressHandler(const GDBMI::ResultRecord& r); + void disassembleMemoryHandler(const MI::ResultRecord& r); + void updateExecutionAddressHandler(const MI::ResultRecord& r); //for str to uint conversion. bool ok; @@ -156,7 +154,8 @@ QSplitter *m_splitter; }; -} +} // end of namespace GDB +} // end of namespace KDevMI /***************************************************************************/ /***************************************************************************/ diff --git a/debuggers/gdb/disassemblewidget.cpp b/debuggers/gdb/disassemblewidget.cpp --- a/debuggers/gdb/disassemblewidget.cpp +++ b/debuggers/gdb/disassemblewidget.cpp @@ -23,8 +23,21 @@ */ #include "disassemblewidget.h" -#include "gdbcommand.h" + #include "debuggerplugin.h" +#include "debuglog.h" +#include "debugsession.h" +#include "mi/micommand.h" +#include "registers/registersmanager.h" + +#include +#include +#include +#include + +#include +#include +#include #include #include @@ -37,24 +50,9 @@ #include #include -#include -#include -#include +using namespace KDevMI::GDB; +using namespace KDevMI::MI; -#include - -#include -#include -#include -#include "debugsession.h" - -#include "registers/registersmanager.h" -#include "debug.h" - -using namespace GDBMI; - -namespace GDBDebugger -{ SelectAddressDialog::SelectAddressDialog(QWidget* parent) : QDialog(parent) @@ -299,10 +297,10 @@ update(currentAddress); } -void DisassembleWidget::updateExecutionAddressHandler(const GDBMI::ResultRecord& r) +void DisassembleWidget::updateExecutionAddressHandler(const ResultRecord& r) { - const GDBMI::Value& content = r["asm_insns"]; - const GDBMI::Value& pc = content[0]; + const Value& content = r["asm_insns"]; + const Value& pc = content[0]; if( pc.hasField("address") ){ QString addr = pc["address"].literal(); address_ = addr.toULong(&ok,16); @@ -322,30 +320,30 @@ //only get $pc if (from.isEmpty()){ s->addCommand( - new GDBCommand(DataDisassemble, "-s \"$pc\" -e \"$pc+1\" -- 0", this, &DisassembleWidget::updateExecutionAddressHandler ) ); + new MICommand(DataDisassemble, "-s \"$pc\" -e \"$pc+1\" -- 0", this, &DisassembleWidget::updateExecutionAddressHandler ) ); }else{ QString cmd = (to.isEmpty())? QString("-s %1 -e \"%1 + 256\" -- 0").arg(from ): QString("-s %1 -e %2+1 -- 0").arg(from).arg(to); // if both addr set s->addCommand( - new GDBCommand(DataDisassemble, cmd, this, &DisassembleWidget::disassembleMemoryHandler ) ); + new MICommand(DataDisassemble, cmd, this, &DisassembleWidget::disassembleMemoryHandler ) ); } } /***************************************************************************/ -void DisassembleWidget::disassembleMemoryHandler(const GDBMI::ResultRecord& r) +void DisassembleWidget::disassembleMemoryHandler(const ResultRecord& r) { - const GDBMI::Value& content = r["asm_insns"]; + const Value& content = r["asm_insns"]; QString currentFunction; m_disassembleWindow->clear(); for(int i = 0; i < content.size(); ++i) { - const GDBMI::Value& line = content[i]; + const Value& line = content[i]; QString addr, fct, offs, inst; @@ -437,6 +435,3 @@ } m_registersManager->updateRegisters(); } - -} - diff --git a/debuggers/gdb/gdb.h b/debuggers/gdb/gdb.h --- a/debuggers/gdb/gdb.h +++ b/debuggers/gdb/gdb.h @@ -2,6 +2,7 @@ * Low level GDB 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 @@ -22,123 +23,22 @@ #ifndef GDB_H_d5c9cb274cbad688fe7a507a84f6633b #define GDB_H_d5c9cb274cbad688fe7a507a84f6633b -#include "mi/gdbmi.h" -#include "mi/miparser.h" -#include "gdbcommand.h" +#include "midebugger.h" -#include +namespace KDevMI { namespace GDB { -#include -#include - -class KConfigGroup; - -namespace GDBDebugger -{ - -class GDB : public QObject +class GdbDebugger : public MIDebugger { Q_OBJECT public: - explicit GDB(QObject* parent = 0); - ~GDB() override; - - /** Starts GDB. This should be done after connecting to all - signals the client is interested in. */ - void start(KConfigGroup& config, const QStringList& extraArguments = {}); - - /** Executes a command. This method may be called at - most once each time 'ready' is emitted. When the - GDB instance is just constructed, one should wait - for 'ready' as well. - - The ownership of 'command' is transferred to GDB. */ - void execute(GDBCommand* command); - - /** Returns true if 'execute' can be called immediately. */ - bool isReady() const; - - /** FIXME: temporary, to be eliminated. */ - GDBCommand* currentCommand() const; - - /** Arrange to gdb 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 GDB. */ - void kill(); - -Q_SIGNALS: - /** Emitted when debugger becomes ready -- i.e. when - isReady call will return true. */ - void ready(); - - /** Emitted when GDB itself exits. This could happen because - it just crashed due to internal bug, or we killed it - explicitly. */ - void gdbExited(); - - /** Emitted when GDB reports stop, with 'r' being the - data provided by GDB. */ - void programStopped(const GDBMI::AsyncRecord& r); - - /** Emitted when GDB 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 GDBMI::StreamRecord& s); - - /** Reports an async notification record. */ - void notification(const GDBMI::AsyncRecord& n); - - /** Emitted for error that is not handled by the - command being executed. */ - void error(const GDBMI::ResultRecord& s); - - /** Reports output from the running application. - Generally output will only be available when - using remote GDB targets. When running locally, - the output will either appear on GDB 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. At the moment, stderr output from - GDB and the 'log' MI channel will be also routed here. */ - void internalCommandOutput(const QString& s); - -private Q_SLOTS: - void readyReadStandardOutput(); - void readyReadStandardError(); - void processFinished(int exitCode, QProcess::ExitStatus exitStatus); - void processErrored(QProcess::ProcessError); - -private: - void processLine(const QByteArray& line); - -private: - QString gdbBinary_; - KProcess* process_; - - GDBCommand* currentCmd_; + explicit GdbDebugger(QObject* parent = 0); + ~GdbDebugger() override; - MIParser mi_parser_; + void start(KConfigGroup& config, const QStringList& extraArguments = {}) override; - /** The unprocessed output from gdb. Output is - processed as soon as we see newline. */ - QByteArray buffer_; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/gdb.cpp b/debuggers/gdb/gdb.cpp --- a/debuggers/gdb/gdb.cpp +++ b/debuggers/gdb/gdb.cpp @@ -3,6 +3,7 @@ * * 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 @@ -21,392 +22,78 @@ */ #include "gdb.h" -#include "debugsession.h" -#include "debug.h" + +#include "dbgglobal.h" +#include "debuglog.h" #include #include +#include #include #include -#include #include #include +#include -#include - -#include -#include - -// #define DEBUG_NO_TRY //to get a backtrace to where the exception was thrown - -Q_LOGGING_CATEGORY(DEBUGGERGDB, "kdevelop.debuggers.gdb") +using namespace KDevMI::GDB; +using namespace KDevMI::MI; -using namespace GDBDebugger; - -GDB::GDB(QObject* parent) -: QObject(parent), process_(0), currentCmd_(0) +GdbDebugger::GdbDebugger(QObject* parent) + : MIDebugger(parent) { } -GDB::~GDB() +GdbDebugger::~GdbDebugger() { - // prevent Qt warning: QProcess: Destroyed while process is still running. - if (process_ && process_->state() == QProcess::Running) { - disconnect(process_, static_cast(&KProcess::error), - this, &GDB::processErrored); - process_->kill(); - process_->waitForFinished(10); - } } -void GDB::start(KConfigGroup& config, const QStringList& extraArguments) +void GdbDebugger::start(KConfigGroup& config, const QStringList& extraArguments) { // FIXME: verify that default value leads to something sensible - QUrl gdbUrl = config.readEntry(GDBDebugger::gdbPathEntry, QUrl()); + QUrl gdbUrl = config.readEntry(gdbPathEntry, QUrl()); if (gdbUrl.isEmpty()) { - gdbBinary_ = "gdb"; + debuggerBinary_ = "gdb"; } else { // FIXME: verify its' a local path. - gdbBinary_ = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); + debuggerBinary_ = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); } - process_ = new KProcess(this); - process_->setOutputChannelMode( KProcess::SeparateChannels ); - connect(process_, &KProcess::readyReadStandardOutput, - this, &GDB::readyReadStandardOutput); - connect(process_, &KProcess::readyReadStandardError, - this, &GDB::readyReadStandardError); - connect(process_, - static_cast(&KProcess::finished), - this, &GDB::processFinished); - connect(process_, static_cast(&KProcess::error), - this, &GDB::processErrored); - QStringList arguments = extraArguments; arguments << "--interpreter=mi2" << "-quiet"; - QUrl shell = config.readEntry(GDBDebugger::debuggerShellEntry, QUrl()); - if( !shell.isEmpty() ) - { + 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 ); + QFileInfo info(shell_without_args); /*if( info.isRelative() ) { shell_without_args = build_dir + "/" + shell_without_args; info.setFile( shell_without_args ); }*/ - if( !info.exists() ) - { + 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, gdbBinary_); + arguments.insert(0, debuggerBinary_); arguments.insert(0, shell.toLocalFile()); - process_->setShellCommand( KShell::joinArgs( arguments ) ); - } - else - { - process_->setProgram( gdbBinary_, arguments ); + process_->setShellCommand(KShell::joinArgs(arguments)); + } else { + process_->setProgram(debuggerBinary_, arguments); } process_->start(); - qCDebug(DEBUGGERGDB) << "STARTING GDB\n"; - emit userCommandOutput(shell.toLocalFile() + ' ' + gdbBinary_ - + " --interpreter=mi2 -quiet\n" ); -} - - -void GDB::execute(GDBCommand* command) -{ - currentCmd_ = command; - QString commandText = currentCmd_->cmdToSend(); - - qCDebug(DEBUGGERGDB) << "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 GDB::isReady() const -{ - return currentCmd_ == 0; -} - -void GDB::interrupt() -{ - //TODO:win32 Porting needed - int pid = process_->pid(); - if (pid != 0) { - ::kill(pid, SIGINT); - } -} - -GDBCommand* GDB::currentCommand() const -{ - return currentCmd_; -} - -void GDB::kill() -{ - process_->kill(); -} - -void GDB::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 GDB::readyReadStandardError() -{ - process_->setReadChannel(QProcess::StandardOutput); - emit internalCommandOutput(QString::fromUtf8(process_->readAll())); -} - -void GDB::processLine(const QByteArray& line) -{ - qCDebug(DEBUGGERGDB) << "GDB output: " << line; - - FileSymbol file; - file.contents = line; - - std::unique_ptr r(mi_parser_.parse(&file)); - - if (!r) - { - // FIXME: Issue an error! - qCDebug(DEBUGGERGDB) << "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 GDBMI::Record::Result: { - GDBMI::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(DEBUGGERGDB) << "Received a result without a pending command"; - } else { - Q_ASSERT(currentCmd_->token() == result.token); - currentCmd_->invokeHandler(result); - } - } - else if (result.reason == "error") - { - qCDebug(DEBUGGERGDB) << "Handling error"; - // Some commands want to handle errors themself. - if (currentCmd_->handlesError() && - currentCmd_->invokeHandler(result)) - { - qCDebug(DEBUGGERGDB) << "Invoked custom handler\n"; - // Done, nothing more needed - } - else - emit error(result); - } - else - { - qCDebug(DEBUGGERGDB) << "Unhandled result code: " << result.reason; - } - - delete currentCmd_; - currentCmd_ = nullptr; - emit ready(); - break; - } - - case GDBMI::Record::Async: { - GDBMI::AsyncRecord& async = dynamic_cast(*r); - - switch (async.subkind) { - case GDBMI::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(DEBUGGERGDB) << "Unhandled exec notification: " << async.reason; - } - break; - } - - case GDBMI::AsyncRecord::Notify: { - // Prefix '='; supplementary information that we should handle (new breakpoint etc.) - emit notification(async); - break; - } - - case GDBMI::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 GDBMI::Record::Stream: { - - GDBMI::StreamRecord& s = dynamic_cast(*r); - - if (s.subkind == GDBMI::StreamRecord::Target) { - emit applicationOutput(s.message); - } else { - if (currentCmd_ && currentCmd_->isUserCommand()) - emit userCommandOutput(s.message); - else if (s.subkind == GDBMI::StreamRecord::Console) { - emit applicationOutput(s.message); - } else { - emit internalCommandOutput(s.message); - } - - if (currentCmd_) - currentCmd_->newOutput(s.message); - } - - emit streamRecord(s); - - break; - } - - case GDBMI::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 GDB::processFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - Q_UNUSED(exitCode); - Q_UNUSED(exitStatus); - qCDebug(DEBUGGERGDB) << "GDB FINISHED\n"; - /* FIXME: return the status? */ - emit gdbExited(); - - -/* FIXME: revive. Need to arrange for controller to delete us. - bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit; - deleteLater(); - delete tty_; - tty_ = 0; - - if (abnormal) - emit debuggerAbnormalExit(); - - raiseEvent(debugger_exited); - - destroyCmds(); - setState(s_dbgNotStarted|s_appNotStarted|s_programExited); - emit showMessage(i18n("Process exited"), 3000); - - emit gdbUserCommandStdout("(gdb) Process exited\n"); -*/ -} - -void GDB::processErrored(QProcess::ProcessError error) -{ - qCDebug(DEBUGGERGDB) << "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.", - gdbBinary_), - i18n("Could not start debugger")); - - /* FIXME: make sure the controller gets rids of GDB instance - emit debuggerAbnormalExit(); - - raiseEvent(debugger_exited); */ - - /* Used to be before, GDB controller might want to do - the same. - destroyCmds(); - setState(s_dbgNotStarted|s_appNotStarted|s_programExited); - emit showMessage(i18n("Process didn't start"), 3000); - */ - emit userCommandOutput("(gdb) didn't start\n"); - } 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")); - } + 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'); } diff --git a/debuggers/gdb/gdbbreakpointcontroller.h b/debuggers/gdb/gdbbreakpointcontroller.h new file mode 100644 --- /dev/null +++ b/debuggers/gdb/gdbbreakpointcontroller.h @@ -0,0 +1,43 @@ +/* + * + * 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 . + * + */ + +#ifndef GDBBREAKPOINTCONTROLLER_H +#define GDBBREAKPOINTCONTROLLER_H + +#include "mibreakpointcontroller.h" + +namespace KDevMI { namespace GDB { + +class DebugSession; +class BreakpointController : public MIBreakpointController +{ + Q_OBJECT + +public: + BreakpointController(DebugSession *parent); +private: +}; + +} // end of namespace GDB +} // end of namespace KDevMI + +#endif // GDBBREAKPOINTCONTROLLER_H diff --git a/debuggers/gdb/gdbbreakpointcontroller.cpp b/debuggers/gdb/gdbbreakpointcontroller.cpp new file mode 100644 --- /dev/null +++ b/debuggers/gdb/gdbbreakpointcontroller.cpp @@ -0,0 +1,32 @@ +/* + * + * 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 "gdbbreakpointcontroller.h" + +#include "debugsession.h" + +using namespace KDevMI::GDB; + +BreakpointController::BreakpointController(DebugSession* parent) + : MIBreakpointController(parent) +{ +} diff --git a/debuggers/gdb/gdbconfigpage.h b/debuggers/gdb/gdbconfigpage.h --- a/debuggers/gdb/gdbconfigpage.h +++ b/debuggers/gdb/gdbconfigpage.h @@ -39,11 +39,14 @@ class IProject; } -namespace GDBDebugger +namespace KDevMI +{ +namespace GDB { class CppDebuggerPlugin; class DebugSession; } +} class GdbConfigPageFactory : public KDevelop::LaunchConfigurationPageFactory { @@ -68,16 +71,16 @@ class GdbLauncher : public KDevelop::ILauncher { public: - GdbLauncher( GDBDebugger::CppDebuggerPlugin* plugin, IExecutePlugin* execute ); + GdbLauncher( KDevMI::GDB::CppDebuggerPlugin* plugin, IExecutePlugin* execute ); QList< KDevelop::LaunchConfigurationPageFactory* > configPages() const override; QString description() const override; QString id() override; QString name() const override; KJob* start(const QString& launchMode, KDevelop::ILaunchConfiguration* cfg) override; QStringList supportedModes() const override; private: QList factoryList; - GDBDebugger::CppDebuggerPlugin* m_plugin; + KDevMI::GDB::CppDebuggerPlugin* m_plugin; IExecutePlugin* m_execute; }; diff --git a/debuggers/gdb/gdbconfigpage.cpp b/debuggers/gdb/gdbconfigpage.cpp --- a/debuggers/gdb/gdbconfigpage.cpp +++ b/debuggers/gdb/gdbconfigpage.cpp @@ -88,36 +88,36 @@ void GdbConfigPage::loadFromConfiguration( const KConfigGroup& cfg, KDevelop::IProject* ) { bool block = blockSignals( true ); - ui->kcfg_gdbPath->setUrl( cfg.readEntry( GDBDebugger::gdbPathEntry, QUrl() ) ); - ui->kcfg_debuggingShell->setUrl( cfg.readEntry( GDBDebugger::debuggerShellEntry, QUrl() ) ); - ui->kcfg_configGdbScript->setUrl( cfg.readEntry( GDBDebugger::remoteGdbConfigEntry, QUrl() ) ); - ui->kcfg_runShellScript->setUrl( cfg.readEntry( GDBDebugger::remoteGdbShellEntry, QUrl() ) ); - ui->kcfg_runGdbScript->setUrl( cfg.readEntry( GDBDebugger::remoteGdbRunEntry, QUrl() ) ); - ui->kcfg_displayStaticMembers->setChecked( cfg.readEntry(GDBDebugger::staticMembersEntry, false) ); - ui->kcfg_asmDemangle->setChecked( cfg.readEntry( GDBDebugger::demangleNamesEntry, true) ); - ui->kcfg_startWith->setCurrentIndex( ui->kcfg_startWith->findData( cfg.readEntry( GDBDebugger::startWithEntry, "ApplicationOutput" ) ) ); + ui->kcfg_gdbPath->setUrl( cfg.readEntry( KDevMI::gdbPathEntry, QUrl() ) ); + ui->kcfg_debuggingShell->setUrl( cfg.readEntry( KDevMI::debuggerShellEntry, QUrl() ) ); + ui->kcfg_configGdbScript->setUrl( cfg.readEntry( KDevMI::remoteGdbConfigEntry, QUrl() ) ); + ui->kcfg_runShellScript->setUrl( cfg.readEntry( KDevMI::remoteGdbShellEntry, QUrl() ) ); + ui->kcfg_runGdbScript->setUrl( cfg.readEntry( KDevMI::remoteGdbRunEntry, QUrl() ) ); + ui->kcfg_displayStaticMembers->setChecked( cfg.readEntry(KDevMI::staticMembersEntry, false) ); + ui->kcfg_asmDemangle->setChecked( cfg.readEntry( KDevMI::demangleNamesEntry, true) ); + ui->kcfg_startWith->setCurrentIndex( ui->kcfg_startWith->findData( cfg.readEntry( KDevMI::startWithEntry, "ApplicationOutput" ) ) ); blockSignals( block ); } void GdbConfigPage::saveToConfiguration( KConfigGroup cfg, KDevelop::IProject* ) const { - cfg.writeEntry(GDBDebugger::gdbPathEntry, ui->kcfg_gdbPath->url() ); - cfg.writeEntry(GDBDebugger::debuggerShellEntry, ui->kcfg_debuggingShell->url() ); - cfg.writeEntry(GDBDebugger::remoteGdbConfigEntry, ui->kcfg_configGdbScript->url() ); - cfg.writeEntry(GDBDebugger::remoteGdbShellEntry, ui->kcfg_runShellScript->url() ); - cfg.writeEntry(GDBDebugger::remoteGdbRunEntry, ui->kcfg_runGdbScript->url() ); - cfg.writeEntry(GDBDebugger::staticMembersEntry, ui->kcfg_displayStaticMembers->isChecked() ); - cfg.writeEntry(GDBDebugger::demangleNamesEntry, ui->kcfg_asmDemangle->isChecked() ); - cfg.writeEntry(GDBDebugger::startWithEntry, ui->kcfg_startWith->itemData( ui->kcfg_startWith->currentIndex() ).toString() ); + cfg.writeEntry(KDevMI::gdbPathEntry, ui->kcfg_gdbPath->url() ); + cfg.writeEntry(KDevMI::debuggerShellEntry, ui->kcfg_debuggingShell->url() ); + cfg.writeEntry(KDevMI::remoteGdbConfigEntry, ui->kcfg_configGdbScript->url() ); + cfg.writeEntry(KDevMI::remoteGdbShellEntry, ui->kcfg_runShellScript->url() ); + cfg.writeEntry(KDevMI::remoteGdbRunEntry, ui->kcfg_runGdbScript->url() ); + cfg.writeEntry(KDevMI::staticMembersEntry, ui->kcfg_displayStaticMembers->isChecked() ); + cfg.writeEntry(KDevMI::demangleNamesEntry, ui->kcfg_asmDemangle->isChecked() ); + cfg.writeEntry(KDevMI::startWithEntry, ui->kcfg_startWith->itemData( ui->kcfg_startWith->currentIndex() ).toString() ); } QString GdbConfigPage::title() const { return i18n( "GDB Configuration" ); } -GdbLauncher::GdbLauncher( GDBDebugger::CppDebuggerPlugin* p, IExecutePlugin* execute ) +GdbLauncher::GdbLauncher( KDevMI::GDB::CppDebuggerPlugin* p, IExecutePlugin* execute ) : m_plugin( p ) , m_execute( execute ) { @@ -165,7 +165,7 @@ { l << depjob; } - l << new GDBDebugger::DebugJob( m_plugin, cfg, m_execute ); + l << new KDevMI::GDB::DebugJob( m_plugin, cfg, m_execute ); return new KDevelop::ExecuteCompositeJob( KDevelop::ICore::self()->runController(), l ); } qWarning() << "Unknown launch mode" << launchMode << "for config:" << cfg->name(); diff --git a/debuggers/gdb/gdbframestackmodel.h b/debuggers/gdb/gdbframestackmodel.h --- a/debuggers/gdb/gdbframestackmodel.h +++ b/debuggers/gdb/gdbframestackmodel.h @@ -1,51 +1,43 @@ /* - * GDB-specific implementation of thread and frame model. - * - * Copyright 2009 Vladimir Prus + * GDB-specific implementation of frame stack model + * 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 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ -#ifndef KDEVELOP_GDB_FRAMESTACKMODEL_H -#define KDEVELOP_GDB_FRAMESTACKMODEL_H +#ifndef FRAMESTACKMODEL_H +#define FRAMESTACKMODEL_H + +#include "miframestackmodel.h" -#include +namespace KDevMI { namespace GDB { -#include "debugsession.h" -using namespace GDBDebugger; +class DebugSession; +class GdbFrameStackModel : public KDevMI::MIFrameStackModel +{ + Q_OBJECT +public: + GdbFrameStackModel(DebugSession* session); -namespace GDBMI { struct ResultRecord; } + DebugSession* session(); +}; -namespace KDevelop { - - class GdbFrameStackModel : public FrameStackModel - { - public: - GdbFrameStackModel(DebugSession* session) : FrameStackModel(session) {} - - public: - DebugSession* session() { return static_cast(FrameStackModel::session()); } - - protected: // FrameStackModel overrides - void fetchThreads() override; - void fetchFrames(int threadNumber, int from, int to) override; - - private: - void handleThreadInfo(const GDBMI::ResultRecord& r); - }; -} +} // end of namespace GDB +} // end of namespace KDevMI -#endif +#endif // FRAMESTACKMODEL_H diff --git a/debuggers/gdb/gdbframestackmodel.cpp b/debuggers/gdb/gdbframestackmodel.cpp --- a/debuggers/gdb/gdbframestackmodel.cpp +++ b/debuggers/gdb/gdbframestackmodel.cpp @@ -1,141 +1,37 @@ /* - * GDB-specific implementation of thread and frame model. - * - * Copyright 2009 Vladimir Prus + * GDB-specific implementation of frame stack model + * 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 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ #include "gdbframestackmodel.h" -#include "gdbcommand.h" - -#include - -using namespace KDevelop; -QString getFunctionOrAddress(const GDBMI::Value &frame) -{ - if (frame.hasField("func")) - return frame["func"].literal(); - else - return frame["addr"].literal(); -} - -QPair getSource(const GDBMI::Value &frame) -{ - QPair ret(QString(), -1); - if (frame.hasField("fullname")) - ret=qMakePair(frame["fullname"].literal(), frame["line"].toInt()-1); - else if (frame.hasField("file")) - ret=qMakePair(frame["file"].literal(), frame["line"].toInt()-1); - else if (frame.hasField("from")) - ret.first=frame["from"].literal(); - - return ret; -} +#include "debugsession.h" -void GdbFrameStackModel::fetchThreads() -{ - session()->addCommand( - new GDBCommand(GDBMI::ThreadInfo, "", - this, - &GdbFrameStackModel::handleThreadInfo)); -} +using namespace KDevMI::GDB; -void GdbFrameStackModel::handleThreadInfo(const GDBMI::ResultRecord& r) +GdbFrameStackModel::GdbFrameStackModel(DebugSession *session) + : MIFrameStackModel(session) { - const GDBMI::Value& threads = r["threads"]; - - // Traverse GDB threads in backward order -- since GDB - // reports them in backward order. We want UI to - // show thread IDs in the natural order. - // FIXME: make the code independent of whatever craziness - // gdb might have tomorrow. - - QList threadsList; - int gidx = threads.size()-1; - for (; gidx >= 0; --gidx) { - KDevelop::FrameStackModel::ThreadItem i; - const GDBMI::Value & threadMI = threads[gidx]; - i.nr = threadMI["id"].toInt(); - if (threadMI["state"].literal() == "stopped") { - i.name = getFunctionOrAddress(threads[gidx]["frame"]); - } else { - i.name = i18n("(running)"); - } - threadsList << i; - } - setThreads(threadsList); - if (r.hasField("current-thread-id")) { - int currentThreadId = r["current-thread-id"].toInt(); - - setCurrentThread(currentThreadId); - - if (session()->hasCrashed()) { - setCrashedThreadIndex(currentThreadId); - } - } } -struct FrameListHandler : public GDBCommandHandler -{ - FrameListHandler(GdbFrameStackModel* model, int thread, int to) - : model(model), m_thread(thread) , m_to(to) {} - - void handle(const GDBMI::ResultRecord &r) override - { - const GDBMI::Value& stack = r["stack"]; - int first = stack[0]["level"].toInt(); - QList frames; - for (int i = 0; i< stack.size(); ++i) { - const GDBMI::Value& frame = stack[i]; - KDevelop::FrameStackModel::FrameItem f; - f.nr = frame["level"].toInt(); - f.name = getFunctionOrAddress(frame); - QPair loc = getSource(frame); - f.file = QUrl::fromLocalFile(loc.first); - f.line = loc.second; - frames << f; - } - bool hasMore = false; - if (!frames.isEmpty()) { - if (frames.last().nr == m_to+1) { - frames.takeLast(); - hasMore = true; - } - } - if (first == 0) { - model->setFrames(m_thread, frames); - } else { - model->insertFrames(m_thread, frames); - } - model->setHasMoreFrames(m_thread, hasMore); - } -private: - GdbFrameStackModel* model; - int m_thread; - int m_to; -}; - -void GdbFrameStackModel::fetchFrames(int threadNumber, int from, int to) +DebugSession* GdbFrameStackModel::session() { - //to+1 so we know if there are more - QString arg = QString("%1 %2").arg(from).arg(to+1); - GDBCommand *c = new GDBCommand(GDBMI::StackListFrames, arg, - new FrameListHandler(this, threadNumber, to)); - c->setThread(threadNumber); - session()->addCommand(c); + return static_cast(FrameStackModel::session()); } diff --git a/debuggers/gdb/gdboutputwidget.h b/debuggers/gdb/gdboutputwidget.h --- a/debuggers/gdb/gdboutputwidget.h +++ b/debuggers/gdb/gdboutputwidget.h @@ -23,13 +23,13 @@ #ifndef _GDBOUTPUTWIDGET_H_ #define _GDBOUTPUTWIDGET_H_ +#include "dbgglobal.h" + +#include +#include #include #include -#include #include -#include - -#include "gdbglobal.h" namespace KDevelop { class IDebugSession; @@ -39,7 +39,9 @@ class QTextEdit; class QToolButton; -namespace GDBDebugger +namespace KDevMI +{ +namespace GDB { class GDBController; @@ -149,6 +151,7 @@ void contextMenuEvent(QContextMenuEvent* event) override; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/gdboutputwidget.cpp b/debuggers/gdb/gdboutputwidget.cpp --- a/debuggers/gdb/gdboutputwidget.cpp +++ b/debuggers/gdb/gdboutputwidget.cpp @@ -23,32 +23,31 @@ #include "gdboutputwidget.h" -#include +#include "dbgglobal.h" +#include "debuggerplugin.h" +#include "debuglog.h" +#include "debugsession.h" + +#include +#include + +#include +#include +#include #include -#include -#include -#include -#include -#include #include #include #include #include -#include +#include +#include +#include +#include #include #include -#include -#include - -#include "gdbglobal.h" -#include "debuggerplugin.h" -#include "debugsession.h" -#include "debug.h" - -namespace GDBDebugger -{ +using namespace KDevMI::GDB; /***************************************************************************/ @@ -129,16 +128,16 @@ DebugSession *session = qobject_cast(s); if (!session) return; connect(this, &GDBOutputWidget::userGDBCmd, - session, &DebugSession::slotUserGDBCmd); + session, &DebugSession::addUserCommand); connect(this, &GDBOutputWidget::breakInto, session, &DebugSession::interruptDebugger); - connect(session, &DebugSession::gdbInternalCommandStdout, + connect(session, &DebugSession::debuggerInternalCommandOutput, this, &GDBOutputWidget::slotInternalCommandStdout); - connect(session, &DebugSession::gdbUserCommandStdout, + connect(session, &DebugSession::debuggerUserCommandOutput, this, &GDBOutputWidget::slotUserCommandStdout); - connect(session, &DebugSession::gdbStateChanged, + connect(session, &DebugSession::debuggerStateChanged, this, &GDBOutputWidget::slotStateChanged); slotStateChanged(s_none, session->debuggerState()); @@ -450,11 +449,3 @@ { return showInternalCommands_; } - -/***************************************************************************/ -/***************************************************************************/ -/***************************************************************************/ -} - - - diff --git a/debuggers/gdb/gdbvariable.h b/debuggers/gdb/gdbvariable.h --- a/debuggers/gdb/gdbvariable.h +++ b/debuggers/gdb/gdbvariable.h @@ -1,81 +1,47 @@ /* - * GDB-specific Variable + * GDB-specific variable + * Copyright 2016 Aetf * - * Copyright 2009 Vladimir Prus - * - * 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 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ #ifndef GDBVARIABLE_H #define GDBVARIABLE_H -#include "mi/gdbmi.h" - -#include - -#include +#include "mivariable.h" +namespace KDevelop { +class TreeModel; +class TreeItem; +} -class CreateVarobjHandler; -class FetchMoreChildrenHandler; +namespace KDevMI { namespace GDB { -namespace KDevelop +class GdbVariable : public KDevMI::MIVariable { - class GdbVariable : public Variable - { - public: - GdbVariable(TreeModel* model, TreeItem* parent, - const QString& expression, - const QString& display = ""); - - ~GdbVariable(); + Q_OBJECT - /* FIXME: should eventually remove, so that existance of - varobjs is fully encapsulalated inside GdbVariable. */ - const QString& varobj() const; - void handleUpdate(const GDBMI::Value& var); +public: + GdbVariable(KDevelop::TreeModel* model, KDevelop::TreeItem* parent, + const QString& expression, const QString& display = ""); +}; - static GdbVariable *findByVarobjName(const QString& varobjName); - - /* Called when GDB dies. Clears the association between varobj names - and Variable instances. */ - static void markAllDead(); - - virtual bool canSetFormat() const override { return true; } - - private: // Variable overrides - void attachMaybe(QObject *callback, const char *callbackMethod) override; - void fetchMoreChildren() override; - void formatChanged() override; - - private: // Internal - friend class ::CreateVarobjHandler; - friend class ::FetchMoreChildrenHandler; - QString enquotedExpression() const; - void setVarobj(const QString& v); - QString varobj_; - - // How many children should be fetched in one - // increment. - static const int fetchStep = 5; - - /* Map from GDB varobj name to GdbVariable. - FIXME: eventually, should be per-session map. */ - static QMap allVariables_; - }; -} +} // end of namespace GDB +} // end of namespace KDevMI -#endif +#endif // GDBVARIABLE_H diff --git a/debuggers/gdb/gdbvariable.cpp b/debuggers/gdb/gdbvariable.cpp --- a/debuggers/gdb/gdbvariable.cpp +++ b/debuggers/gdb/gdbvariable.cpp @@ -1,383 +1,32 @@ /* - * GDB-specific Variable + * GDB-specific variable + * Copyright 2016 Aetf * - * Copyright 2009 Vladimir Prus - * - * 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 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ #include "gdbvariable.h" -#include "gdbcommand.h" -#include "debugsession.h" - -#include -#include using namespace KDevelop; -using namespace GDBDebugger; - -QMap GdbVariable::allVariables_; - -static bool hasStartedSession() -{ - if (!ICore::self()->debugController()) return false; //happens on shutdown - - IDebugSession *session = ICore::self()->debugController()->currentSession(); - if (!session) - return false; - - IDebugSession::DebuggerState s = session->state(); - return s != IDebugSession::NotStartedState - && s != IDebugSession::EndedState; -} - -GdbVariable::GdbVariable(TreeModel* model, TreeItem* parent, - const QString& expression, const QString& display) -: Variable(model, parent, expression, display) -{ -} - -GdbVariable::~GdbVariable() -{ - if (!varobj_.isEmpty()) - { - // Delete only top-level variable objects. - if (topLevel()) { - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); - s->addCommand(new GDBCommand(GDBMI::VarDelete, - QString("\"%1\"").arg(varobj_))); - } - } - allVariables_.remove(varobj_); - } -} - -GdbVariable* GdbVariable::findByVarobjName(const QString& varobjName) -{ - if (allVariables_.count(varobjName) == 0) - return 0; - return allVariables_[varobjName]; -} - -void GdbVariable::setVarobj(const QString& v) -{ - if (!varobj_.isEmpty()) { - // this should not happen - // but apperently it does when attachMaybe is called a second time before - // the first -var-create call returned - allVariables_.remove(varobj_); - } - varobj_ = v; - allVariables_[varobj_] = this; -} - - -static int nextId = 0; - -class CreateVarobjHandler : public GDBCommandHandler -{ -public: - CreateVarobjHandler(GdbVariable *variable, QObject *callback, const char *callbackMethod) - : m_variable(variable), m_callback(callback), m_callbackMethod(callbackMethod) - {} - - void handle(const GDBMI::ResultRecord &r) override - { - if (!m_variable) return; - bool hasValue = false; - GdbVariable* variable = m_variable.data(); - variable->deleteChildren(); - variable->setInScope(true); - if (r.reason == "error") { - variable->setShowError(true); - } else { - variable->setVarobj(r["name"].literal()); - - bool hasMore = false; - if (r.hasField("has_more") && r["has_more"].toInt()) - // GDB swears there are more children. Trust it - hasMore = true; - else - // There are no more children in addition to what - // numchild reports. But, in KDevelop, the variable - // is not yet expanded, and those numchild are not - // fetched yet. So, if numchild != 0, hasMore should - // be true. - hasMore = r["numchild"].toInt() != 0; - - variable->setHasMore(hasMore); - - variable->setType(r["type"].literal()); - variable->setValue(r["value"].literal()); - hasValue = !r["value"].literal().isEmpty(); - if (variable->isExpanded() && r["numchild"].toInt()) { - variable->fetchMoreChildren(); - } - - if (variable->format() != KDevelop::Variable::Natural) { - //TODO doesn't work for children as they are not yet loaded - variable->formatChanged(); - } - } - - if (m_callback && m_callbackMethod) { - QMetaObject::invokeMethod(m_callback, m_callbackMethod, Q_ARG(bool, hasValue)); - } - } - bool handlesError() override { return true; } - -private: - QPointer m_variable; - QObject *m_callback; - const char *m_callbackMethod; -}; - -void GdbVariable::attachMaybe(QObject *callback, const char *callbackMethod) -{ - if (!varobj_.isEmpty()) - return; - - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); - s->addCommand( - new GDBCommand( - GDBMI::VarCreate, - QString("var%1 @ %2").arg(nextId++).arg(enquotedExpression()), - new CreateVarobjHandler(this, callback, callbackMethod))); - } -} - -void GdbVariable::markAllDead() -{ - QMap::iterator i, e; - for (i = allVariables_.begin(), e = allVariables_.end(); i != e; ++i) - { - i.value()->varobj_.clear(); - } - allVariables_.clear(); -} - -class FetchMoreChildrenHandler : public GDBCommandHandler -{ -public: - FetchMoreChildrenHandler(GdbVariable *variable, DebugSession *session) - : m_variable(variable), m_session(session), m_activeCommands(1) - {} - - void handle(const GDBMI::ResultRecord &r) override - { - if (!m_variable) return; - --m_activeCommands; - - GdbVariable* variable = m_variable.data(); - - if (r.hasField("children")) - { - const GDBMI::Value& children = r["children"]; - for (int i = 0; i < children.size(); ++i) { - const GDBMI::Value& child = children[i]; - const QString& exp = child["exp"].literal(); - if (exp == "public" || exp == "protected" || exp == "private") { - ++m_activeCommands; - m_session->addCommand( - new GDBCommand(GDBMI::VarListChildren, - QString("--all-values \"%1\"") - .arg(child["name"].literal()), - this/*use again as handler*/)); - } else { - KDevelop::Variable* xvar = m_session->variableController()-> - createVariable(variable->model(), variable, - child["exp"].literal()); - GdbVariable* var = static_cast(xvar); - var->setTopLevel(false); - var->setVarobj(child["name"].literal()); - bool hasMore = child["numchild"].toInt() != 0 || ( child.hasField("dynamic") && child["dynamic"].toInt()!=0 ); - var->setHasMoreInitial(hasMore); - variable->appendChild(var); - var->setType(child["type"].literal()); - var->setValue(child["value"].literal()); - } - } - } - - /* Note that we don't set hasMore to true if there are still active - commands. The reason is that we don't want the user to have - even theoretical ability to click on "..." item and confuse - us. */ - bool hasMore = false; - if (r.hasField("has_more")) - hasMore = r["has_more"].toInt(); - - variable->setHasMore(hasMore); - if (m_activeCommands == 0) { - variable->emitAllChildrenFetched(); - delete this; - } - } - bool handlesError() override { - // FIXME: handle error? - return false; - } - bool autoDelete() override { - // we delete ourselve - return false; - } - -private: - QPointer m_variable; - DebugSession *m_session; - int m_activeCommands; -}; - -void GdbVariable::fetchMoreChildren() -{ - int c = childItems.size(); - // FIXME: should not even try this if app is not started. - // Probably need to disable open, or something - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); - s->addCommand( - new GDBCommand(GDBMI::VarListChildren, - QString("--all-values \"%1\" %2 %3").arg(varobj_) - .arg( c ).arg( c + fetchStep ), // fetch from .. to .. - new FetchMoreChildrenHandler(this, s))); - } -} - -void GdbVariable::handleUpdate(const GDBMI::Value& var) -{ - if (var.hasField("type_changed") - && var["type_changed"].literal() == "true") - { - deleteChildren(); - // FIXME: verify that this check is right. - setHasMore(var["new_num_children"].toInt() != 0); - fetchMoreChildren(); - } - - if (var.hasField("in_scope") && var["in_scope"].literal() == "false") - { - setInScope(false); - } - else - { - setInScope(true); - - if (var.hasField("new_num_children")) { - int nc = var["new_num_children"].toInt(); - Q_ASSERT(nc != -1); - setHasMore(false); - while (childCount() > nc) { - TreeItem *c = child(childCount()-1); - removeChild(childCount()-1); - delete c; - } - } - - // FIXME: the below code is essentially copy-paste from - // FetchMoreChildrenHandler. We need to introduce GDB-specific - // subclass of KDevelop::Variable that is capable of creating - // itself from MI output directly, and relay to that. - if (var.hasField("new_children")) - { - const GDBMI::Value& children = var["new_children"]; - for (int i = 0; i < children.size(); ++i) { - const GDBMI::Value& child = children[i]; - const QString& exp = child["exp"].literal(); - - IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); - KDevelop::Variable* xvar = s->variableController()-> - createVariable(model(), this, exp); - GdbVariable* var = static_cast(xvar); - var->setTopLevel(false); - var->setVarobj(child["name"].literal()); - bool hasMore = child["numchild"].toInt() != 0 || ( child.hasField("dynamic") && child["dynamic"].toInt()!=0 ); - var->setHasMoreInitial(hasMore); - appendChild(var); - var->setType(child["type"].literal()); - var->setValue(child["value"].literal()); - var->setChanged(true); - } - } - - if (var.hasField("type_changed") && var["type_changed"].literal() == "true") { - setType(var["new_type"].literal()); - } - setValue(var["value"].literal()); - setChanged(true); - setHasMore(var.hasField("has_more") && var["has_more"].toInt()); - } -} - -const QString& GdbVariable::varobj() const -{ - return varobj_; -} - -QString GdbVariable::enquotedExpression() const -{ - QString expr = expression(); - expr.replace('"', "\\\""); - expr = expr.prepend('"').append('"'); - return expr; -} - - -class SetFormatHandler : public GDBCommandHandler -{ -public: - SetFormatHandler(GdbVariable *var) - : m_variable(var) - {} - - void handle(const GDBMI::ResultRecord &r) override - { - if(r.hasField("value")) - m_variable.data()->setValue(r["value"].literal()); - } -private: - QPointer m_variable; -}; +using namespace KDevMI::GDB; -void GdbVariable::formatChanged() +GdbVariable::GdbVariable(TreeModel *model, TreeItem *parent, + const QString& expression, const QString& display) + : MIVariable(model, parent, expression, display) { - if(childCount()) - { - foreach(TreeItem* item, childItems) { - Q_ASSERT(dynamic_cast(item)); - if( GdbVariable* var=dynamic_cast(item)) - var->setFormat(format()); - } - } - else - { - if (hasStartedSession()) { - IDebugSession* is = ICore::self()->debugController()->currentSession(); - DebugSession* s = static_cast(is); - s->addCommand( - new GDBCommand(GDBMI::VarSetFormat, - QString(" \"%1\" %2 ").arg(varobj_).arg(format2str(format())), - new SetFormatHandler(this) - ) - ); - } - } } diff --git a/debuggers/gdb/memviewdlg.h b/debuggers/gdb/memviewdlg.h --- a/debuggers/gdb/memviewdlg.h +++ b/debuggers/gdb/memviewdlg.h @@ -16,20 +16,23 @@ #ifndef _MEMVIEW_H_ #define _MEMVIEW_H_ -#include "mi/gdbmi.h" +#include "dbgglobal.h" +#include "mi/mi.h" #include +#include -#include "gdbglobal.h" namespace KDevelop { class IDebugSession; } class QLineEdit; class QToolBox; -namespace GDBDebugger +namespace KDevMI +{ +namespace GDB { class CppDebuggerPlugin; class MemoryView; @@ -72,7 +75,7 @@ private: // Callbacks void sizeComputed(const QString& value); - void memoryRead(const GDBMI::ResultRecord& r); + void memoryRead(const MI::ResultRecord& r); private Q_SLOTS: void memoryEdited(int start, int end); @@ -113,6 +116,8 @@ private slots: void currentSessionChanged(KDevelop::IDebugSession* session); }; -} + +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/memviewdlg.cpp b/debuggers/gdb/memviewdlg.cpp --- a/debuggers/gdb/memviewdlg.cpp +++ b/debuggers/gdb/memviewdlg.cpp @@ -14,517 +14,513 @@ ***************************************************************************/ #include "memviewdlg.h" -#include "gdbcommand.h" -#include "gdbglobal.h" -#include -#include +#include "dbgglobal.h" +#include "debugsession.h" +#include "mi/micommand.h" + +#include +#include + #include +#include + +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include +#include #include - -#include +#include #include -#include -#include -#include +#include +#include #include -#include - -#include -#include -#include - -#include +#include -#include -#include +using namespace KDevMI::GDB; +using KDevMI::MI::CommandType; -#include "debugsession.h" +/** Container for controls that select memory range. -namespace GDBDebugger + The memory range selection is embedded into memory view widget, + it's not a standalone dialog. However, we want to have easy way + to hide/show all controls, so we group them in this class. +*/ +class MemoryRangeSelector : public QWidget { - /** Container for controls that select memory range. +public: + QLineEdit* startAddressLineEdit; + QLineEdit* amountLineEdit; + QPushButton* okButton; + QPushButton* cancelButton; - The memory range selection is embedded into memory view widget, - it's not a standalone dialog. However, we want to have easy way - to hide/show all controls, so we group them in this class. - */ - class MemoryRangeSelector : public QWidget + MemoryRangeSelector(QWidget* parent) + : QWidget(parent) { - public: - QLineEdit* startAddressLineEdit; - QLineEdit* amountLineEdit; - QPushButton* okButton; - QPushButton* cancelButton; - - MemoryRangeSelector(QWidget* parent) - : QWidget(parent) - { - QVBoxLayout* l = new QVBoxLayout(this); + QVBoxLayout* l = new QVBoxLayout(this); - // Grid layout: labels + address field - QGridLayout* gl = new QGridLayout(this); - l->addLayout(gl); + // Grid layout: labels + address field + QGridLayout* gl = new QGridLayout(this); + l->addLayout(gl); - QLabel* l1 = new QLabel(i18n("Start"), this); - gl->addWidget(l1, 0, 1); + QLabel* l1 = new QLabel(i18n("Start"), this); + gl->addWidget(l1, 0, 1); - startAddressLineEdit = new QLineEdit(this); - gl->addWidget(startAddressLineEdit, 0, 3); + startAddressLineEdit = new QLineEdit(this); + gl->addWidget(startAddressLineEdit, 0, 3); - QLabel* l2 = new QLabel(i18n("Amount"), this); - gl->addWidget(l2, 2, 1); + QLabel* l2 = new QLabel(i18n("Amount"), this); + gl->addWidget(l2, 2, 1); - amountLineEdit = new QLineEdit(this); - gl->addWidget(amountLineEdit, 2, 3); + amountLineEdit = new QLineEdit(this); + gl->addWidget(amountLineEdit, 2, 3); - l->addSpacing(2); + l->addSpacing(2); - QHBoxLayout* hb = new QHBoxLayout(this); - l->addLayout(hb); - hb->addStretch(); + QHBoxLayout* hb = new QHBoxLayout(this); + l->addLayout(hb); + hb->addStretch(); - okButton = new QPushButton(i18n("OK"), this); - hb->addWidget(okButton); + okButton = new QPushButton(i18n("OK"), this); + hb->addWidget(okButton); - cancelButton = new QPushButton(i18n("Cancel"), this); - hb->addWidget(cancelButton); + cancelButton = new QPushButton(i18n("Cancel"), this); + hb->addWidget(cancelButton); - l->addSpacing(2); - setLayout(l); + l->addSpacing(2); + setLayout(l); - connect(startAddressLineEdit, SIGNAL(returnPressed()), - okButton, SLOT(animateClick())); + connect(startAddressLineEdit, SIGNAL(returnPressed()), + okButton, SLOT(animateClick())); - connect(amountLineEdit, SIGNAL(returnPressed()), - okButton, SLOT(animateClick())); - } - }; + connect(amountLineEdit, SIGNAL(returnPressed()), + okButton, SLOT(animateClick())); + } +}; - MemoryView::MemoryView(QWidget* parent) - : QWidget(parent), - // New memory view can be created only when debugger is active, - // so don't set s_appNotStarted here. - khexedit2_widget(0), - amount_(0), data_(0), - debuggerState_(0) - { - setWindowTitle(i18n("Memory view")); - emit captionChanged(windowTitle()); +MemoryView::MemoryView(QWidget* parent) +: QWidget(parent), + // New memory view can be created only when debugger is active, + // so don't set s_appNotStarted here. + khexedit2_widget(0), + amount_(0), data_(0), + debuggerState_(0) +{ + setWindowTitle(i18n("Memory view")); + emit captionChanged(windowTitle()); + + initWidget(); + + if (isOk()) + slotEnableOrDisable(); + + connect(KDevelop::ICore::self()->debugController(), + SIGNAL(currentSessionChanged(KDevelop::IDebugSession*)), + SLOT(currentSessionChanged(KDevelop::IDebugSession*))); +} + +void MemoryView::currentSessionChanged(KDevelop::IDebugSession* s) +{ + DebugSession *session = qobject_cast(s); + if (!session) return; + connect(session, + SIGNAL(gdbStateChanged(DBGStateFlags,DBGStateFlags)), + SLOT(slotStateChanged(DBGStateFlags,DBGStateFlags))); +} - initWidget(); +void MemoryView::slotStateChanged(DBGStateFlags oldState, DBGStateFlags newState) +{ + Q_UNUSED(oldState); + debuggerStateChanged(newState); +} - if (isOk()) - slotEnableOrDisable(); +void MemoryView::initWidget() +{ + QVBoxLayout *l = new QVBoxLayout(this); - connect(KDevelop::ICore::self()->debugController(), - SIGNAL(currentSessionChanged(KDevelop::IDebugSession*)), - SLOT(currentSessionChanged(KDevelop::IDebugSession*))); + khexedit2_widget = KHE::createBytesEditWidget(this); + if (!khexedit2_widget) + { + QTextEdit* edit = new QTextEdit(this); + l->addWidget(edit); + + edit->setText( + "

Not Available

" + "

Could not open a KHexEdit2 interface. " + "Installing Okteta should provide the required components.

"); + return; } - void MemoryView::currentSessionChanged(KDevelop::IDebugSession* s) + KHE::BytesEditInterface *bytesEdit = KHE::bytesEditInterface(khexedit2_widget); + if (bytesEdit) { - DebugSession *session = qobject_cast(s); - if (!session) return; - connect(session, - SIGNAL(gdbStateChanged(DBGStateFlags,DBGStateFlags)), - SLOT(slotStateChanged(DBGStateFlags,DBGStateFlags))); + bytesEdit->setReadOnly(false); + bytesEdit->setOverwriteMode(true); + bytesEdit->setOverwriteOnly(true); + bytesEdit->setAutoDelete(false); } - void MemoryView::slotStateChanged(DBGStateFlags oldState, DBGStateFlags newState) + KHE::ValueColumnInterface *valueColumn = KHE::valueColumnInterface(khexedit2_widget); + if (valueColumn) { - Q_UNUSED(oldState); - debuggerStateChanged(newState); + valueColumn->setCoding(KHE::ValueColumnInterface::HexadecimalCoding); + valueColumn->setNoOfGroupedBytes(4); + valueColumn->setByteSpacingWidth(2); + valueColumn->setGroupSpacingWidth(12); + valueColumn->setResizeStyle(KHE::ValueColumnInterface::LockGrouping); } - void MemoryView::initWidget() + KHE::CharColumnInterface *charColumn = KHE::charColumnInterface(khexedit2_widget); + if(charColumn) { - QVBoxLayout *l = new QVBoxLayout(this); + charColumn->setShowUnprintable(false); + charColumn->setSubstituteChar('*'); + } - khexedit2_widget = KHE::createBytesEditWidget(this); - if (!khexedit2_widget) - { - QTextEdit* edit = new QTextEdit(this); - l->addWidget(edit); - - edit->setText( - "

Not Available

" - "

Could not open a KHexEdit2 interface. " - "Installing Okteta should provide the required components.

"); - return; - } + rangeSelector_ = new MemoryRangeSelector(this); + l->addWidget(rangeSelector_); - KHE::BytesEditInterface *bytesEdit = KHE::bytesEditInterface(khexedit2_widget); - if (bytesEdit) - { - bytesEdit->setReadOnly(false); - bytesEdit->setOverwriteMode(true); - bytesEdit->setOverwriteOnly(true); - bytesEdit->setAutoDelete(false); - } + connect(rangeSelector_->okButton, SIGNAL(clicked()), + this, SLOT(slotChangeMemoryRange())); - KHE::ValueColumnInterface *valueColumn = KHE::valueColumnInterface(khexedit2_widget); - if (valueColumn) - { - valueColumn->setCoding(KHE::ValueColumnInterface::HexadecimalCoding); - valueColumn->setNoOfGroupedBytes(4); - valueColumn->setByteSpacingWidth(2); - valueColumn->setGroupSpacingWidth(12); - valueColumn->setResizeStyle(KHE::ValueColumnInterface::LockGrouping); - } + connect(rangeSelector_->cancelButton, SIGNAL(clicked()), + this, SLOT(slotHideRangeDialog())); - KHE::CharColumnInterface *charColumn = KHE::charColumnInterface(khexedit2_widget); - if(charColumn) - { - charColumn->setShowUnprintable(false); - charColumn->setSubstituteChar('*'); - } + connect(rangeSelector_->startAddressLineEdit, + SIGNAL(textChanged(QString)), + this, + SLOT(slotEnableOrDisable())); - rangeSelector_ = new MemoryRangeSelector(this); - l->addWidget(rangeSelector_); + connect(rangeSelector_->amountLineEdit, + SIGNAL(textChanged(QString)), + this, + SLOT(slotEnableOrDisable())); - connect(rangeSelector_->okButton, SIGNAL(clicked()), - this, SLOT(slotChangeMemoryRange())); + l->addWidget(khexedit2_widget); +} - connect(rangeSelector_->cancelButton, SIGNAL(clicked()), - this, SLOT(slotHideRangeDialog())); +void MemoryView::debuggerStateChanged(DBGStateFlags state) +{ + if (isOk()) + { + debuggerState_ = state; + slotEnableOrDisable(); + } +} - connect(rangeSelector_->startAddressLineEdit, - SIGNAL(textChanged(QString)), - this, - SLOT(slotEnableOrDisable())); - connect(rangeSelector_->amountLineEdit, - SIGNAL(textChanged(QString)), - this, - SLOT(slotEnableOrDisable())); +void MemoryView::slotHideRangeDialog() +{ + rangeSelector_->hide(); +} - l->addWidget(khexedit2_widget); - } +void MemoryView::slotChangeMemoryRange() +{ + DebugSession *session = qobject_cast( + KDevelop::ICore::self()->debugController()->currentSession()); + if (!session) return; - void MemoryView::debuggerStateChanged(DBGStateFlags state) - { - if (isOk()) - { - debuggerState_ = state; - slotEnableOrDisable(); - } - } + session->addCommand(new ExpressionValueCommand( + rangeSelector_->amountLineEdit->text(), + this, &MemoryView::sizeComputed)); +} +void MemoryView::sizeComputed(const QString& size) +{ + DebugSession *session = qobject_cast( + KDevelop::ICore::self()->debugController()->currentSession()); + if (!session) return; + + session->addCommand(new GDBCommand(MI::DataReadMemory, + QString("%1 x 1 1 %2") + .arg(rangeSelector_->startAddressLineEdit->text()) + .arg(size), + this, + &MemoryView::memoryRead)); +} + +void MemoryView::memoryRead(const MI::ResultRecord& r) +{ + const MI::Value& content = r["memory"][0]["data"]; + bool startStringConverted; + start_ = r["addr"].literal().toULongLong(&startStringConverted, 16); + amount_ = content.size(); - void MemoryView::slotHideRangeDialog() + startAsString_ = rangeSelector_->startAddressLineEdit->text(); + amountAsString_ = rangeSelector_->amountLineEdit->text(); + + setWindowTitle(i18np("%2 (1 byte)","%2 (%1 bytes)",amount_,startAsString_)); + emit captionChanged(windowTitle()); + + KHE::BytesEditInterface* bytesEditor = KHE::bytesEditInterface(khexedit2_widget); + bytesEditor->setData(this->data_, 0); + + delete[] this->data_; + this->data_ = new char[amount_]; + for(int i = 0; i < content.size(); ++i) { - rangeSelector_->hide(); + this->data_[i] = content[i].literal().toInt(0, 16); } - void MemoryView::slotChangeMemoryRange() - { - DebugSession *session = qobject_cast( - KDevelop::ICore::self()->debugController()->currentSession()); - if (!session) return; + bytesEditor->setData(this->data_, amount_); + + slotHideRangeDialog(); +} - session->addCommand(new ExpressionValueCommand( - rangeSelector_->amountLineEdit->text(), - this, &MemoryView::sizeComputed)); - } - void MemoryView::sizeComputed(const QString& size) +void MemoryView::memoryEdited(int start, int end) +{ + DebugSession *session = qobject_cast( + KDevelop::ICore::self()->debugController()->currentSession()); + if (!session) return; + + for(int i = start; i <= end; ++i) { - DebugSession *session = qobject_cast( - KDevelop::ICore::self()->debugController()->currentSession()); - if (!session) return; - - session->addCommand(new GDBCommand(GDBMI::DataReadMemory, - QString("%1 x 1 1 %2") - .arg(rangeSelector_->startAddressLineEdit->text()) - .arg(size), - this, - &MemoryView::memoryRead)); + session->addCommand(new GDBCommand(MI::GdbSet, + QString("*(char*)(%1 + %2) = %3") + .arg(start_) + .arg(i) + .arg(QString::number(data_[i])))); } +} - void MemoryView::memoryRead(const GDBMI::ResultRecord& r) - { - const GDBMI::Value& content = r["memory"][0]["data"]; - bool startStringConverted; - start_ = r["addr"].literal().toULongLong(&startStringConverted, 16); - amount_ = content.size(); +void MemoryView::contextMenuEvent(QContextMenuEvent *e) +{ + if (!isOk()) + return; - startAsString_ = rangeSelector_->startAddressLineEdit->text(); - amountAsString_ = rangeSelector_->amountLineEdit->text(); + KHE::BytesEditInterface *bytesEdit = KHE::bytesEditInterface(khexedit2_widget); + KHE::ValueColumnInterface *valueColumn = KHE::valueColumnInterface(khexedit2_widget); - setWindowTitle(i18np("%2 (1 byte)","%2 (%1 bytes)",amount_,startAsString_)); - emit captionChanged(windowTitle()); + QMenu menu; - KHE::BytesEditInterface* bytesEditor = KHE::bytesEditInterface(khexedit2_widget); - bytesEditor->setData(this->data_, 0); + bool app_running = !(debuggerState_ & s_appNotStarted); - delete[] this->data_; - this->data_ = new char[amount_]; - for(int i = 0; i < content.size(); ++i) + QAction* reload = menu.addAction(i18n("&Reload")); + reload->setIcon(QIcon::fromTheme("view-refresh")); + reload->setEnabled(app_running && amount_ != 0); + + QActionGroup *formatGroup = NULL; + QActionGroup *groupingGroup = NULL; + if (valueColumn) + { + // make Format menu with action group + QMenu *formatMenu = new QMenu(i18n("&Format")); + formatGroup = new QActionGroup(formatMenu); + + QAction *binary = formatGroup->addAction(i18n("&Binary")); + binary->setData(KHE::ValueColumnInterface::BinaryCoding); + binary->setShortcut(Qt::Key_B); + formatMenu->addAction(binary); + + QAction *octal = formatGroup->addAction(i18n("&Octal")); + octal->setData(KHE::ValueColumnInterface::OctalCoding); + octal->setShortcut(Qt::Key_O); + formatMenu->addAction(octal); + + QAction *decimal = formatGroup->addAction(i18n("&Decimal")); + decimal->setData(KHE::ValueColumnInterface::DecimalCoding); + decimal->setShortcut(Qt::Key_D); + formatMenu->addAction(decimal); + + QAction *hex = formatGroup->addAction(i18n("&Hexadecimal")); + hex->setData(KHE::ValueColumnInterface::HexadecimalCoding); + hex->setShortcut(Qt::Key_H); + formatMenu->addAction(hex); + + foreach(QAction* act, formatGroup->actions()) { - this->data_[i] = content[i].literal().toInt(0, 16); + act->setCheckable(true); + act->setChecked(act->data().toInt() == valueColumn->coding()); + act->setShortcutContext(Qt::WidgetWithChildrenShortcut); } - bytesEditor->setData(this->data_, amount_); - - slotHideRangeDialog(); - } + menu.addMenu(formatMenu); - void MemoryView::memoryEdited(int start, int end) - { - DebugSession *session = qobject_cast( - KDevelop::ICore::self()->debugController()->currentSession()); - if (!session) return; + // make Grouping menu with action group + QMenu *groupingMenu = new QMenu(i18n("&Grouping")); + groupingGroup = new QActionGroup(groupingMenu); - for(int i = start; i <= end; ++i) - { - session->addCommand(new GDBCommand(GDBMI::GdbSet, - QString("*(char*)(%1 + %2) = %3") - .arg(start_) - .arg(i) - .arg(QString::number(data_[i])))); - } - } + QAction *group0 = groupingGroup->addAction(i18n("&0")); + group0->setData(0); + group0->setShortcut(Qt::Key_0); + groupingMenu->addAction(group0); - void MemoryView::contextMenuEvent(QContextMenuEvent *e) - { - if (!isOk()) - return; + QAction *group1 = groupingGroup->addAction(i18n("&1")); + group1->setData(1); + group1->setShortcut(Qt::Key_1); + groupingMenu->addAction(group1); - KHE::BytesEditInterface *bytesEdit = KHE::bytesEditInterface(khexedit2_widget); - KHE::ValueColumnInterface *valueColumn = KHE::valueColumnInterface(khexedit2_widget); + QAction *group2 = groupingGroup->addAction(i18n("&2")); + group2->setData(2); + group2->setShortcut(Qt::Key_2); + groupingMenu->addAction(group2); - QMenu menu; + QAction *group4 = groupingGroup->addAction(i18n("&4")); + group4->setData(4); + group4->setShortcut(Qt::Key_4); + groupingMenu->addAction(group4); - bool app_running = !(debuggerState_ & s_appNotStarted); + QAction *group8 = groupingGroup->addAction(i18n("&8")); + group8->setData(8); + group8->setShortcut(Qt::Key_8); + groupingMenu->addAction(group8); - QAction* reload = menu.addAction(i18n("&Reload")); - reload->setIcon(QIcon::fromTheme("view-refresh")); - reload->setEnabled(app_running && amount_ != 0); + QAction *group16 = groupingGroup->addAction(i18n("1&6")); + group16->setData(16); + group16->setShortcut(Qt::Key_6); + groupingMenu->addAction(group16); - QActionGroup *formatGroup = NULL; - QActionGroup *groupingGroup = NULL; - if (valueColumn) + foreach(QAction* act, groupingGroup->actions()) { - // make Format menu with action group - QMenu *formatMenu = new QMenu(i18n("&Format")); - formatGroup = new QActionGroup(formatMenu); - - QAction *binary = formatGroup->addAction(i18n("&Binary")); - binary->setData(KHE::ValueColumnInterface::BinaryCoding); - binary->setShortcut(Qt::Key_B); - formatMenu->addAction(binary); - - QAction *octal = formatGroup->addAction(i18n("&Octal")); - octal->setData(KHE::ValueColumnInterface::OctalCoding); - octal->setShortcut(Qt::Key_O); - formatMenu->addAction(octal); - - QAction *decimal = formatGroup->addAction(i18n("&Decimal")); - decimal->setData(KHE::ValueColumnInterface::DecimalCoding); - decimal->setShortcut(Qt::Key_D); - formatMenu->addAction(decimal); - - QAction *hex = formatGroup->addAction(i18n("&Hexadecimal")); - hex->setData(KHE::ValueColumnInterface::HexadecimalCoding); - hex->setShortcut(Qt::Key_H); - formatMenu->addAction(hex); - - foreach(QAction* act, formatGroup->actions()) - { - act->setCheckable(true); - act->setChecked(act->data().toInt() == valueColumn->coding()); - act->setShortcutContext(Qt::WidgetWithChildrenShortcut); - } - - menu.addMenu(formatMenu); - - - // make Grouping menu with action group - QMenu *groupingMenu = new QMenu(i18n("&Grouping")); - groupingGroup = new QActionGroup(groupingMenu); - - QAction *group0 = groupingGroup->addAction(i18n("&0")); - group0->setData(0); - group0->setShortcut(Qt::Key_0); - groupingMenu->addAction(group0); - - QAction *group1 = groupingGroup->addAction(i18n("&1")); - group1->setData(1); - group1->setShortcut(Qt::Key_1); - groupingMenu->addAction(group1); - - QAction *group2 = groupingGroup->addAction(i18n("&2")); - group2->setData(2); - group2->setShortcut(Qt::Key_2); - groupingMenu->addAction(group2); - - QAction *group4 = groupingGroup->addAction(i18n("&4")); - group4->setData(4); - group4->setShortcut(Qt::Key_4); - groupingMenu->addAction(group4); - - QAction *group8 = groupingGroup->addAction(i18n("&8")); - group8->setData(8); - group8->setShortcut(Qt::Key_8); - groupingMenu->addAction(group8); - - QAction *group16 = groupingGroup->addAction(i18n("1&6")); - group16->setData(16); - group16->setShortcut(Qt::Key_6); - groupingMenu->addAction(group16); - - foreach(QAction* act, groupingGroup->actions()) - { - act->setCheckable(true); - act->setChecked(act->data().toInt() == valueColumn->noOfGroupedBytes()); - act->setShortcutContext(Qt::WidgetWithChildrenShortcut); - } - - menu.addMenu(groupingMenu); + act->setCheckable(true); + act->setChecked(act->data().toInt() == valueColumn->noOfGroupedBytes()); + act->setShortcutContext(Qt::WidgetWithChildrenShortcut); } - QAction* write = menu.addAction(i18n("Write changes")); - write->setIcon(QIcon::fromTheme("document-save")); - write->setEnabled(app_running && bytesEdit && bytesEdit->isModified()); - - QAction* range = menu.addAction(i18n("Change memory range")); - range->setEnabled(app_running && !rangeSelector_->isVisible()); - range->setIcon(QIcon::fromTheme("document-edit")); + menu.addMenu(groupingMenu); + } - QAction* close = menu.addAction(i18n("Close this view")); - close->setIcon(QIcon::fromTheme("window-close")); + QAction* write = menu.addAction(i18n("Write changes")); + write->setIcon(QIcon::fromTheme("document-save")); + write->setEnabled(app_running && bytesEdit && bytesEdit->isModified()); + QAction* range = menu.addAction(i18n("Change memory range")); + range->setEnabled(app_running && !rangeSelector_->isVisible()); + range->setIcon(QIcon::fromTheme("document-edit")); - QAction* result = menu.exec(e->globalPos()); + QAction* close = menu.addAction(i18n("Close this view")); + close->setIcon(QIcon::fromTheme("window-close")); - if (result == reload) - { - // We use numeric start_ and amount_ stored in this, - // not textual startAsString_ and amountAsString_, - // because program position might have changes and expressions - // are no longer valid. - DebugSession *session = qobject_cast( - KDevelop::ICore::self()->debugController()->currentSession()); - if (session) { - session->addCommand(new GDBCommand(GDBMI::DataReadMemory, - QString("%1 x 1 1 %2").arg(start_).arg(amount_), - this, - &MemoryView::memoryRead)); - } - } + QAction* result = menu.exec(e->globalPos()); - if (result && formatGroup && formatGroup == result->actionGroup()) - valueColumn->setCoding((KHE::ValueColumnInterface::KCoding)result->data().toInt()); - if (result && groupingGroup && groupingGroup == result->actionGroup()) - valueColumn->setNoOfGroupedBytes(result->data().toInt()); - - if (result == write) - { - memoryEdited(0, amount_); - bytesEdit->setModified(false); + if (result == reload) + { + // We use numeric start_ and amount_ stored in this, + // not textual startAsString_ and amountAsString_, + // because program position might have changes and expressions + // are no longer valid. + DebugSession *session = qobject_cast( + KDevelop::ICore::self()->debugController()->currentSession()); + if (session) { + session->addCommand(new GDBCommand(MI::DataReadMemory, + QString("%1 x 1 1 %2").arg(start_).arg(amount_), + this, + &MemoryView::memoryRead)); } + } - if (result == range) - { - rangeSelector_->startAddressLineEdit->setText(startAsString_); - rangeSelector_->amountLineEdit->setText(amountAsString_); + if (result && formatGroup && formatGroup == result->actionGroup()) + valueColumn->setCoding((KHE::ValueColumnInterface::KCoding)result->data().toInt()); - rangeSelector_->show(); - rangeSelector_->startAddressLineEdit->setFocus(); - } + if (result && groupingGroup && groupingGroup == result->actionGroup()) + valueColumn->setNoOfGroupedBytes(result->data().toInt()); - if (result == close) - delete this; + if (result == write) + { + memoryEdited(0, amount_); + bytesEdit->setModified(false); } - bool MemoryView::isOk() const + if (result == range) { - return khexedit2_widget; + rangeSelector_->startAddressLineEdit->setText(startAsString_); + rangeSelector_->amountLineEdit->setText(amountAsString_); + + rangeSelector_->show(); + rangeSelector_->startAddressLineEdit->setFocus(); } - void MemoryView::slotEnableOrDisable() - { - bool app_started = !(debuggerState_ & s_appNotStarted); + if (result == close) + delete this; +} - bool enabled_ = app_started && - !rangeSelector_->startAddressLineEdit->text().isEmpty() && - !rangeSelector_->amountLineEdit->text().isEmpty(); +bool MemoryView::isOk() const +{ + return khexedit2_widget; +} - rangeSelector_->okButton->setEnabled(enabled_); - } +void MemoryView::slotEnableOrDisable() +{ + bool app_started = !(debuggerState_ & s_appNotStarted); + bool enabled_ = app_started && + !rangeSelector_->startAddressLineEdit->text().isEmpty() && + !rangeSelector_->amountLineEdit->text().isEmpty(); - MemoryViewerWidget::MemoryViewerWidget(CppDebuggerPlugin* /*plugin*/, QWidget* parent) - : QWidget(parent) - { - setWindowIcon(QIcon::fromTheme("server-database", windowIcon())); - setWindowTitle(i18n("Memory viewer")); + rangeSelector_->okButton->setEnabled(enabled_); +} - QAction * newMemoryViewerAction = new QAction(this); - newMemoryViewerAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); - newMemoryViewerAction->setText(i18n("New memory viewer")); - newMemoryViewerAction->setToolTip(i18nc("@info:tooltip", "Open a new memory viewer.")); - newMemoryViewerAction->setIcon(QIcon::fromTheme("window-new")); - connect(newMemoryViewerAction, SIGNAL(triggered(bool)), this, SLOT(slotAddMemoryView())); - addAction(newMemoryViewerAction); - QVBoxLayout *l = new QVBoxLayout(this); +MemoryViewerWidget::MemoryViewerWidget(CppDebuggerPlugin* /*plugin*/, QWidget* parent) +: QWidget(parent) +{ + setWindowIcon(QIcon::fromTheme("server-database", windowIcon())); + setWindowTitle(i18n("Memory viewer")); - toolBox_ = new QToolBox(this); - l->addWidget(toolBox_); + QAction * newMemoryViewerAction = new QAction(this); + newMemoryViewerAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + newMemoryViewerAction->setText(i18n("New memory viewer")); + newMemoryViewerAction->setToolTip(i18nc("@info:tooltip", "Open a new memory viewer.")); + newMemoryViewerAction->setIcon(QIcon::fromTheme("window-new")); + connect(newMemoryViewerAction, SIGNAL(triggered(bool)), this, SLOT(slotAddMemoryView())); + addAction(newMemoryViewerAction); - // Start with one empty memory view. - slotAddMemoryView(); - } + QVBoxLayout *l = new QVBoxLayout(this); - void MemoryViewerWidget::slotAddMemoryView() - { - MemoryView* widget = new MemoryView(this); - toolBox_->addItem(widget, widget->windowTitle()); - toolBox_->setCurrentIndex(toolBox_->indexOf(widget)); - memoryViews_.push_back(widget); + toolBox_ = new QToolBox(this); + l->addWidget(toolBox_); - connect(widget, SIGNAL(captionChanged(QString)), - this, SLOT(slotChildCaptionChanged(QString))); + // Start with one empty memory view. + slotAddMemoryView(); +} - connect(widget, SIGNAL(destroyed(QObject*)), - this, SLOT(slotChildDestroyed(QObject*))); - } +void MemoryViewerWidget::slotAddMemoryView() +{ + MemoryView* widget = new MemoryView(this); + toolBox_->addItem(widget, widget->windowTitle()); + toolBox_->setCurrentIndex(toolBox_->indexOf(widget)); + memoryViews_.push_back(widget); - void MemoryViewerWidget::slotChildCaptionChanged(const QString& caption) - { - const QWidget* s = static_cast(sender()); - QWidget* ncs = const_cast(s); - QString cap = caption; - // Prevent intepreting '&' as accelerator specifier. - cap.replace('&', "&&"); - toolBox_->setItemText(toolBox_->indexOf(ncs), cap); - } + connect(widget, SIGNAL(captionChanged(QString)), + this, SLOT(slotChildCaptionChanged(QString))); - void MemoryViewerWidget::slotChildDestroyed(QObject* child) + connect(widget, SIGNAL(destroyed(QObject*)), + this, SLOT(slotChildDestroyed(QObject*))); +} + +void MemoryViewerWidget::slotChildCaptionChanged(const QString& caption) +{ + const QWidget* s = static_cast(sender()); + QWidget* ncs = const_cast(s); + QString cap = caption; + // Prevent intepreting '&' as accelerator specifier. + cap.replace('&', "&&"); + toolBox_->setItemText(toolBox_->indexOf(ncs), cap); +} + +void MemoryViewerWidget::slotChildDestroyed(QObject* child) +{ + QList::iterator i, e; + for(i = memoryViews_.begin(), e = memoryViews_.end(); i != e; ++i) { - QList::iterator i, e; - for(i = memoryViews_.begin(), e = memoryViews_.end(); i != e; ++i) + if (*i == child) { - if (*i == child) - { - memoryViews_.erase(i); - break; - } + memoryViews_.erase(i); + break; } } - } - diff --git a/debuggers/gdb/mi/Makefile.am b/debuggers/gdb/mi/Makefile.am deleted file mode 100644 --- a/debuggers/gdb/mi/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ - -# We need exceptions since they are used to report all MI access errors. -KDE_CXXFLAGS = $(USE_EXCEPTIONS) - -METASOURCES = AUTO -INCLUDES = $(all_includes) - -lib_LTLIBRARIES = libgdbmi_parser.la -libgdbmi_parser_la_LDFLAGS = $(all_libraries) -libgdbmi_parser_la_SOURCES = gdbmi.cpp miparser.cpp milexer.cpp - diff --git a/debuggers/gdb/printers/tests/qtprinters.h b/debuggers/gdb/printers/tests/qtprinters.h --- a/debuggers/gdb/printers/tests/qtprinters.h +++ b/debuggers/gdb/printers/tests/qtprinters.h @@ -21,9 +21,6 @@ #include -namespace GDBDebugger -{ - class QtPrintersTest : public QObject { Q_OBJECT @@ -50,6 +47,4 @@ void testKDevelopTypes(); }; -} - #endif diff --git a/debuggers/gdb/printers/tests/qtprinters.cpp b/debuggers/gdb/printers/tests/qtprinters.cpp --- a/debuggers/gdb/printers/tests/qtprinters.cpp +++ b/debuggers/gdb/printers/tests/qtprinters.cpp @@ -28,9 +28,6 @@ const QString BINARY_PATH(PRINTER_BIN_DIR); -namespace GDBDebugger -{ - class GdbProcess : private QProcess { public: @@ -453,6 +450,4 @@ QVERIFY(gdb.execute("print path2").contains("(\"http://www.test.com\", \"tmp\", \"asdf.txt\")")); } -} -QTEST_MAIN(GDBDebugger::QtPrintersTest) - +QTEST_MAIN(QtPrintersTest) diff --git a/debuggers/gdb/processselection.h b/debuggers/gdb/processselection.h --- a/debuggers/gdb/processselection.h +++ b/debuggers/gdb/processselection.h @@ -26,7 +26,9 @@ class KSysGuardProcessList; class QPushButton; -namespace GDBDebugger +namespace KDevMI +{ +namespace GDB { class ProcessSelectionDialog : public QDialog { @@ -36,15 +38,16 @@ ~ProcessSelectionDialog() override; long int pidSelected(); QSize sizeHint() const override; - + private slots: void selectionChanged(); - + private: KSysGuardProcessList* m_processList; QPushButton* m_okButton; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/processselection.cpp b/debuggers/gdb/processselection.cpp --- a/debuggers/gdb/processselection.cpp +++ b/debuggers/gdb/processselection.cpp @@ -19,22 +19,24 @@ */ #include "processselection.h" + #include #include -#include -#include + +#include +#include + #include -#include +#include #include +#include +#include #include #include -#include +#include -#include -#include -namespace GDBDebugger -{ +using namespace KDevMI::GDB; ProcessSelectionDialog::ProcessSelectionDialog(QWidget *parent) : QDialog(parent) @@ -78,9 +80,9 @@ { QList ps=m_processList->selectedProcesses(); Q_ASSERT(ps.count()==1); - + KSysGuard::Process* process=ps.first(); - + return process->pid(); } @@ -93,5 +95,3 @@ { m_okButton->setEnabled(true); } - -} diff --git a/debuggers/gdb/registers/converters.h b/debuggers/gdb/registers/converters.h --- a/debuggers/gdb/registers/converters.h +++ b/debuggers/gdb/registers/converters.h @@ -22,7 +22,9 @@ #include "registercontroller.h" -namespace GDBDebugger +namespace KDevMI +{ +namespace GDB { class Converters @@ -35,5 +37,6 @@ static QString modeToString(Mode mode); }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif // CONVERTERS_H diff --git a/debuggers/gdb/registers/converters.cpp b/debuggers/gdb/registers/converters.cpp --- a/debuggers/gdb/registers/converters.cpp +++ b/debuggers/gdb/registers/converters.cpp @@ -21,8 +21,7 @@ #include -namespace GDBDebugger -{ +using namespace KDevMI::GDB; QString Converters::formatToString(Format format) { @@ -61,5 +60,3 @@ static const QString modes[LAST_MODE] = {"natural", "v4_float", "v2_double", "v4_int32", "v2_int64", "u32", "u64", "f32", "f64"}; return modes[mode]; } - -} diff --git a/debuggers/gdb/registers/modelsmanager.h b/debuggers/gdb/registers/modelsmanager.h --- a/debuggers/gdb/registers/modelsmanager.h +++ b/debuggers/gdb/registers/modelsmanager.h @@ -35,7 +35,8 @@ class QStandardItem; class QModelIndex; -namespace GDBDebugger +namespace KDevMI { +namespace GDB { class Models; class IRegisterController; @@ -98,5 +99,6 @@ KConfigGroup m_config; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif // MODELSMANAGER_H diff --git a/debuggers/gdb/registers/modelsmanager.cpp b/debuggers/gdb/registers/modelsmanager.cpp --- a/debuggers/gdb/registers/modelsmanager.cpp +++ b/debuggers/gdb/registers/modelsmanager.cpp @@ -26,8 +26,7 @@ #include -namespace GDBDebugger -{ +namespace KDevMI { namespace GDB { struct Model { Model(); @@ -62,6 +61,11 @@ QVector m_models; }; +} // end of namespace GDB +} // end of namespace KDevMI + +using namespace KDevMI::GDB; + ModelsManager::ModelsManager(QObject* parent) : QObject(parent), m_models(new Models), m_controller(0), m_config(KSharedConfig::openConfig()->group("Register models")) {} ModelsManager::~ModelsManager() {} @@ -350,5 +354,3 @@ } } } - -} diff --git a/debuggers/gdb/registers/registercontroller.h b/debuggers/gdb/registers/registercontroller.h --- a/debuggers/gdb/registers/registercontroller.h +++ b/debuggers/gdb/registers/registercontroller.h @@ -27,12 +27,13 @@ #include #include -namespace GDBMI +namespace KDevMI { +namespace MI { struct ResultRecord; } -namespace GDBDebugger +namespace GDB { class DebugSession; @@ -209,15 +210,15 @@ private : ///Handles initialization of register's names. - void registerNamesHandler(const GDBMI::ResultRecord& r); + void registerNamesHandler(const MI::ResultRecord& r); ///Parses new values for general registers from @p r and updates it in m_registers. ///Emits registersChanged signal. - void generalRegistersHandler(const GDBMI::ResultRecord& r); + void generalRegistersHandler(const MI::ResultRecord& r); ///Parses new values for structured registers from @p r and updates it in m_registers. ///Emits registersChanged signal. - virtual void structuredRegistersHandler(const GDBMI::ResultRecord& r); + virtual void structuredRegistersHandler(const MI::ResultRecord& r); private: @@ -238,12 +239,13 @@ DebugSession* m_debugSession; }; -} +} // end of namespace GDB +} // end of namespace KDevMI -Q_DECLARE_TYPEINFO(GDBDebugger::Register, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(GDBDebugger::RegistersGroup, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(GDBDebugger::FlagRegister, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(GDBDebugger::GroupsName, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(GDBDebugger::FormatsModes, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KDevMI::GDB::Register, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KDevMI::GDB::RegistersGroup, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KDevMI::GDB::FlagRegister, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KDevMI::GDB::GroupsName, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KDevMI::GDB::FormatsModes, Q_MOVABLE_TYPE); #endif diff --git a/debuggers/gdb/registers/registercontroller.cpp b/debuggers/gdb/registers/registercontroller.cpp --- a/debuggers/gdb/registers/registercontroller.cpp +++ b/debuggers/gdb/registers/registercontroller.cpp @@ -20,27 +20,26 @@ #include "registercontroller.h" -#include -#include - - -#include "../debugsession.h" -#include "../gdbcommand.h" -#include "../mi/gdbmi.h" -#include "../debug.h" #include "converters.h" +#include "debuglog.h" +#include "mi/mi.h" +#include "mi/micommand.h" +#include "../debugsession.h" -namespace GDBDebugger -{ +#include +#include + +using namespace KDevMI::MI; +using namespace KDevMI::GDB; void IRegisterController::setSession(DebugSession* debugSession) { m_debugSession = debugSession; } void IRegisterController::updateRegisters(const GroupsName& group) { - if (!m_debugSession || m_debugSession->stateIsOn(s_dbgNotStarted | s_shuttingDown)) { + if (!m_debugSession || m_debugSession->debuggerStateIsOn(s_dbgNotStarted | s_shuttingDown)) { return; } @@ -106,39 +105,39 @@ return; } - void (IRegisterController::* handler)(const GDBMI::ResultRecord&); + void (IRegisterController::* handler)(const ResultRecord&); if (group.type() == structured && currentFormat != Raw) { handler = &IRegisterController::structuredRegistersHandler; } else { handler = &IRegisterController::generalRegistersHandler; } - m_debugSession->addCommand(new GDBCommand(GDBMI::DataListRegisterValues, registers, this, handler)); + m_debugSession->addCommand(new MICommand(DataListRegisterValues, registers, this, handler)); } -void IRegisterController::registerNamesHandler(const GDBMI::ResultRecord& r) +void IRegisterController::registerNamesHandler(const ResultRecord& r) { - const GDBMI::Value& names = r["register-names"]; + const Value& names = r["register-names"]; m_rawRegisterNames.clear(); for (int i = 0; i < names.size(); ++i) { - const GDBMI::Value& entry = names[i]; + const Value& entry = names[i]; m_rawRegisterNames.push_back(entry.literal()); } //When here probably request for updating registers was sent, but m_rawRegisterNames were not initialized yet, so it wasn't successful. Update everything once again. updateRegisters(); } -void IRegisterController::generalRegistersHandler(const GDBMI::ResultRecord& r) +void IRegisterController::generalRegistersHandler(const ResultRecord& r) { Q_ASSERT(!m_rawRegisterNames.isEmpty()); QString registerName; - const GDBMI::Value& values = r["register-values"]; + const Value& values = r["register-values"]; for (int i = 0; i < values.size(); ++i) { - const GDBMI::Value& entry = values[i]; + const Value& entry = values[i]; int number = entry["number"].literal().toInt(); Q_ASSERT(m_rawRegisterNames.size() > number); @@ -182,12 +181,12 @@ bool IRegisterController::initializeRegisters() { - if (!m_debugSession || m_debugSession->stateIsOn(s_dbgNotStarted | s_shuttingDown)) { + if (!m_debugSession || m_debugSession->debuggerStateIsOn(s_dbgNotStarted | s_shuttingDown)) { return false; } m_debugSession->addCommand( - new GDBCommand(GDBMI::DataListRegisterNames, "", this, &IRegisterController::registerNamesHandler)); + new MICommand(DataListRegisterNames, "", this, &IRegisterController::registerNamesHandler)); return true; } @@ -235,14 +234,14 @@ void IRegisterController::setGeneralRegister(const Register& reg, const GroupsName& group) { - if (!m_debugSession || m_debugSession->stateIsOn(s_dbgNotStarted | s_shuttingDown)) { + if (!m_debugSession || m_debugSession->debuggerStateIsOn(s_dbgNotStarted | s_shuttingDown)) { return; } const QString command = QString("set var $%1=%2").arg(reg.name).arg(reg.value); qCDebug(DEBUGGERGDB) << "Setting register: " << command; - m_debugSession->addCommand(new GDBCommand(GDBMI::NonMI, command)); + m_debugSession->addCommand(new MICommand(NonMI, command)); updateRegisters(group); } @@ -331,7 +330,7 @@ setGeneralRegister(r, group); } -void IRegisterController::structuredRegistersHandler(const GDBMI::ResultRecord& r) +void IRegisterController::structuredRegistersHandler(const ResultRecord& r) { //Parsing records in format like: //{u8 = {0, 0, 128, 146, 0, 48, 197, 65}, u16 = {0, 37504, 12288, 16837}, u32 = {2457862144, 1103441920}, u64 = 4739246961893310464, f32 = {-8.07793567e-28, 24.6484375}, f64 = 710934821} @@ -343,12 +342,12 @@ QString registerName; Mode currentMode = LAST_MODE; GroupsName group; - const GDBMI::Value& values = r["register-values"]; + const Value& values = r["register-values"]; Q_ASSERT(!m_rawRegisterNames.isEmpty()); for (int i = 0; i < values.size(); ++i) { - const GDBMI::Value& entry = values[i]; + const Value& entry = values[i]; int number = entry["number"].literal().toInt(); registerName = m_rawRegisterNames[number]; if (currentMode == LAST_MODE) { @@ -406,5 +405,3 @@ } } } - -} diff --git a/debuggers/gdb/registers/registercontroller_arm.h b/debuggers/gdb/registers/registercontroller_arm.h --- a/debuggers/gdb/registers/registercontroller_arm.h +++ b/debuggers/gdb/registers/registercontroller_arm.h @@ -24,7 +24,8 @@ #include "registercontroller.h" -namespace GDBDebugger +namespace KDevMI { +namespace GDB { class DebugSession; @@ -68,5 +69,7 @@ bool m_registerNamesInitialized; }; -} +} // end of namespace GDB +} // end of namespace KDevMI + #endif // REGISTERCONTROLLER_ARM_H diff --git a/debuggers/gdb/registers/registercontroller_arm.cpp b/debuggers/gdb/registers/registercontroller_arm.cpp --- a/debuggers/gdb/registers/registercontroller_arm.cpp +++ b/debuggers/gdb/registers/registercontroller_arm.cpp @@ -19,12 +19,11 @@ */ #include "registercontroller_arm.h" -#include "../debug.h" +#include "debuglog.h" #include -namespace GDBDebugger -{ +using namespace KDevMI::GDB; QVector RegisterController_Arm::m_registerNames; FlagRegister RegisterController_Arm::m_cpsr; @@ -184,5 +183,3 @@ return QStringList(); } - -} diff --git a/debuggers/gdb/registers/registercontroller_x86.h b/debuggers/gdb/registers/registercontroller_x86.h --- a/debuggers/gdb/registers/registercontroller_x86.h +++ b/debuggers/gdb/registers/registercontroller_x86.h @@ -23,7 +23,9 @@ #include "registercontroller.h" -namespace GDBDebugger +namespace KDevMI +{ +namespace GDB { class DebugSession; @@ -87,6 +89,8 @@ private: void initRegisterNames(); }; -} + +} // end of namespace GDB +} // end of namespace KDevMI #endif // REGISTERCONTROLLER_X86_H diff --git a/debuggers/gdb/registers/registercontroller_x86.cpp b/debuggers/gdb/registers/registercontroller_x86.cpp --- a/debuggers/gdb/registers/registercontroller_x86.cpp +++ b/debuggers/gdb/registers/registercontroller_x86.cpp @@ -19,12 +19,11 @@ */ #include "registercontroller_x86.h" -#include "../debug.h" +#include "debuglog.h" #include -namespace GDBDebugger -{ +using namespace KDevMI::GDB; QVector RegisterControllerGeneral_x86::m_registerNames; FlagRegister RegisterControllerGeneral_x86::m_eflags; @@ -205,5 +204,3 @@ return QStringList(); } - -} diff --git a/debuggers/gdb/registers/registersmanager.h b/debuggers/gdb/registers/registersmanager.h --- a/debuggers/gdb/registers/registersmanager.h +++ b/debuggers/gdb/registers/registersmanager.h @@ -25,12 +25,13 @@ #include #include -namespace GDBMI +namespace KDevMI { +namespace MI { struct ResultRecord; } -namespace GDBDebugger +namespace GDB { class RegistersView; @@ -58,7 +59,7 @@ private: - void registerNamesHandler(const GDBMI::ResultRecord& r); + void registerNamesHandler(const MI::ResultRecord& r); void parseArchitecture(); QStringList m_registerNames; @@ -97,5 +98,6 @@ bool m_needToCheckArch; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif // REGISTERSMANAGER_H diff --git a/debuggers/gdb/registers/registersmanager.cpp b/debuggers/gdb/registers/registersmanager.cpp --- a/debuggers/gdb/registers/registersmanager.cpp +++ b/debuggers/gdb/registers/registersmanager.cpp @@ -20,20 +20,17 @@ #include "registersmanager.h" -#include "registercontroller_x86.h" #include "registercontroller_arm.h" +#include "registercontroller_x86.h" #include "registersview.h" +#include "debuglog.h" +#include "mi/micommand.h" #include "modelsmanager.h" - -#include "../gdbcommand.h" #include "../debugsession.h" -#include "../debug.h" - - -namespace GDBDebugger -{ +using namespace KDevMI::MI; +using namespace KDevMI::GDB; void ArchitectureParser::parseArchitecture() { @@ -55,13 +52,13 @@ emit architectureParsed(arch); } -void ArchitectureParser::registerNamesHandler(const GDBMI::ResultRecord& r) +void ArchitectureParser::registerNamesHandler(const ResultRecord& r) { - const GDBMI::Value& names = r["register-names"]; + const Value& names = r["register-names"]; m_registerNames.clear(); for (int i = 0; i < names.size(); ++i) { - const GDBMI::Value& entry = names[i]; + const Value& entry = names[i]; if (!entry.literal().isEmpty()) { m_registerNames << entry.literal(); } @@ -72,12 +69,12 @@ void ArchitectureParser::determineArchitecture(DebugSession* debugSession) { - if (!debugSession || debugSession->stateIsOn(s_dbgNotStarted | s_shuttingDown)) { + if (!debugSession || debugSession->debuggerStateIsOn(s_dbgNotStarted | s_shuttingDown)) { return; } debugSession->addCommand( - new GDBCommand(GDBMI::DataListRegisterNames, "", this, &ArchitectureParser::registerNamesHandler)); + new MICommand(DataListRegisterNames, "", this, &ArchitectureParser::registerNamesHandler)); } RegistersManager::RegistersManager(QWidget* parent) @@ -141,7 +138,7 @@ void RegistersManager::updateRegisters() { - if (!m_debugSession || m_debugSession->stateIsOn(s_dbgNotStarted | s_shuttingDown)) { + if (!m_debugSession || m_debugSession->debuggerStateIsOn(s_dbgNotStarted | s_shuttingDown)) { return; } @@ -172,5 +169,3 @@ m_registersView->enable(c ? true : false); } - -} diff --git a/debuggers/gdb/registers/registersview.h b/debuggers/gdb/registers/registersview.h --- a/debuggers/gdb/registers/registersview.h +++ b/debuggers/gdb/registers/registersview.h @@ -28,7 +28,8 @@ class QMenu; class QSignalMapper; -namespace GDBDebugger +namespace KDevMI { +namespace GDB { class ModelsManager; @@ -89,5 +90,7 @@ QVector m_actions; }; -} +} // end of namespace GDB +} // end of namespace KDevMI + #endif diff --git a/debuggers/gdb/registers/registersview.cpp b/debuggers/gdb/registers/registersview.cpp --- a/debuggers/gdb/registers/registersview.cpp +++ b/debuggers/gdb/registers/registersview.cpp @@ -20,18 +20,18 @@ #include "registersview.h" -#include "modelsmanager.h" #include "converters.h" -#include "../debug.h" +#include "debuglog.h" +#include "modelsmanager.h" #include #include #include #include -namespace GDBDebugger -{ +using namespace KDevMI::GDB; + namespace { const int TABLES_COUNT = 5; @@ -246,5 +246,3 @@ m_mapper->setMapping(a, a->text()); connect(a, &QAction::triggered, m_mapper, static_cast(&QSignalMapper::map)); } - -} diff --git a/debuggers/gdb/selectcoredialog.h b/debuggers/gdb/selectcoredialog.h --- a/debuggers/gdb/selectcoredialog.h +++ b/debuggers/gdb/selectcoredialog.h @@ -27,7 +27,8 @@ #include "ui_selectcoredialog.h" -namespace GDBDebugger { +namespace KDevMI { +namespace GDB { class SelectCoreDialog : public QDialog { @@ -40,6 +41,7 @@ Ui::SelectCoreDialog m_ui; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif diff --git a/debuggers/gdb/selectcoredialog.cpp b/debuggers/gdb/selectcoredialog.cpp --- a/debuggers/gdb/selectcoredialog.cpp +++ b/debuggers/gdb/selectcoredialog.cpp @@ -22,7 +22,7 @@ #include "selectcoredialog.h" #include -namespace GDBDebugger { +using namespace KDevMI::GDB; SelectCoreDialog::SelectCoreDialog(QWidget* parent) : QDialog(parent) @@ -41,6 +41,3 @@ { return m_ui.coreFile->url(); } - - -} diff --git a/debuggers/gdb/unittests/test_gdb.h b/debuggers/gdb/unittests/test_gdb.h --- a/debuggers/gdb/unittests/test_gdb.h +++ b/debuggers/gdb/unittests/test_gdb.h @@ -20,13 +20,14 @@ #ifndef GDBTEST_H #define GDBTEST_H -#include #include +#include namespace KDevelop { class TestCore; } -namespace GDBDebugger { +namespace KDevMI { +namespace GDB { class GdbTest : public QObject { @@ -97,13 +98,14 @@ void testPathWithSpace(); private: - bool waitForState(GDBDebugger::DebugSession *session, + bool waitForState(DebugSession *session, KDevelop::IDebugSession::DebuggerState state, const char *file, int line, bool waitForIdle = false); IExecutePlugin* m_iface; }; -} +} // end of namespace GDB +} // end of namespace KDevMI #endif // GDBTEST_H diff --git a/debuggers/gdb/unittests/test_gdb.cpp b/debuggers/gdb/unittests/test_gdb.cpp --- a/debuggers/gdb/unittests/test_gdb.cpp +++ b/debuggers/gdb/unittests/test_gdb.cpp @@ -19,42 +19,42 @@ #include "test_gdb.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include +#include "debugsession.h" +#include "gdbframestackmodel.h" +#include "mi/micommand.h" +#include "mi/milexer.h" +#include "mi/miparser.h" -#include -#include -#include -#include +#include #include +#include +#include #include +#include +#include +#include #include #include -#include -#include -#include #include -#include +#include +#include -#include "gdbcommand.h" -#include "debugsession.h" -#include "gdbframestackmodel.h" -#include -#include +#include +#include +#include + +#include +#include +#include +#include #include +#include +#include +#include using KDevelop::AutoTestShell; -namespace GDBDebugger { +namespace KDevMI { namespace GDB { QUrl findExecutable(const QString& name) { @@ -152,7 +152,7 @@ KConfig *c; }; -class TestFrameStackModel : public KDevelop::GdbFrameStackModel +class TestFrameStackModel : public GdbFrameStackModel { public: @@ -180,7 +180,7 @@ public: TestDebugSession() : DebugSession() { - setTesting(true); + setSourceInitFile(false); m_frameStackModel = new TestFrameStackModel(this); KDevelop::ICore::self()->debugController()->addSession(this); } @@ -275,10 +275,10 @@ { TestDebugSession *session = new TestDebugSession; - QSignalSpy outputSpy(session, SIGNAL(applicationStandardOutputLines(QStringList))); + QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); TestLaunchConfiguration cfg; - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); { @@ -298,7 +298,7 @@ KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->stepInto(); @@ -340,7 +340,7 @@ KDevelop::ICore::self()->debugController()->breakpointModel()->blockSignals(false); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), thirdBreak->line()); @@ -366,7 +366,7 @@ KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 27); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); @@ -399,15 +399,15 @@ TestLaunchConfiguration cfg; QCOMPARE(breakpoints()->rowCount(), 0); - //add breakpoint before startProgram + //add breakpoint before startDebugging breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); QCOMPARE(breakpoints()->rowCount(), 1); breakpoints()->removeRow(0); QCOMPARE(breakpoints()->rowCount(), 0); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 22); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); breakpoints()->removeRow(0); session->run(); @@ -425,7 +425,7 @@ KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("test_gdb.cpp")), 10); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::PendingState); session->run(); @@ -437,23 +437,26 @@ TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; + // breakpoint 1: line 29 KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); QCOMPARE(breakpoints()->rowCount(), 1); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); + // breakpoint 2: line 28 //insert custom command as user might do it using GDB console - session->addCommand(new UserCommand(GDBMI::NonMI, "break "+debugeeFileName+":28")); + session->addCommand(new MI::UserCommand(MI::NonMI, "break "+debugeeFileName+":28")); - WAIT_FOR_STATE(session, DebugSession::PausedState); - QTest::qWait(100); + WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop at line 28 session->stepInto(); - WAIT_FOR_STATE(session, DebugSession::PausedState); + WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop after step QCOMPARE(breakpoints()->rowCount(), 2); b = breakpoints()->breakpoint(1); QCOMPARE(b->url(), QUrl::fromLocalFile(debugeeFileName)); QCOMPARE(b->line(), 27); session->run(); + WAIT_FOR_STATE(session, DebugSession::PausedState); // stop at line 29 + session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } @@ -467,7 +470,7 @@ KDevelop::Breakpoint * b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 22); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b2->hitCount() == 1); @@ -492,7 +495,7 @@ b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR(session, session->state() == DebugSession::PausedState && session->line() == 24); b->setCondition("i == 0"); @@ -514,7 +517,7 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); @@ -539,7 +542,7 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); @@ -568,7 +571,7 @@ KDevelop::Breakpoint *b = breakpoints()->addReadWatchpoint("foo::i"); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->line(), 23); @@ -584,7 +587,7 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); @@ -610,7 +613,7 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 24); @@ -640,7 +643,7 @@ TestLaunchConfiguration cfg(findExecutable("debugeeslow")); QString fileName = findSourceFile("debugeeslow.cpp"); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::ActiveState); QTest::qWait(2000); @@ -661,7 +664,7 @@ TestLaunchConfiguration cfg(findExecutable("debugeeslow")); QString fileName = findSourceFile("debugeeslow.cpp"); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::ActiveState); QTest::qWait(2000); @@ -689,7 +692,7 @@ breakpoints()->addCodeBreakpoint("main"); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); session->run(); @@ -703,30 +706,30 @@ breakpoints()->addCodeBreakpoint("main"); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 27); breakpoints()->removeRows(0, 1); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->rowCount(), 0); - session->addCommand(GDBMI::NonMI, "break debugee.cpp:23"); + session->addCommand(MI::NonMI, "break debugee.cpp:23"); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->rowCount(), 1); - Breakpoint* b = breakpoints()->breakpoint(0); + KDevelop::Breakpoint* b = breakpoints()->breakpoint(0); QCOMPARE(b->line(), 22); - session->addCommand(GDBMI::NonMI, "disable 2"); - session->addCommand(GDBMI::NonMI, "condition 2 i == 1"); - session->addCommand(GDBMI::NonMI, "ignore 2 1"); + session->addCommand(MI::NonMI, "disable 2"); + session->addCommand(MI::NonMI, "condition 2 i == 1"); + session->addCommand(MI::NonMI, "ignore 2 1"); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->enabled(), false); QCOMPARE(b->condition(), QString("i == 1")); QCOMPARE(b->ignoreHits(), 1); - session->addCommand(GDBMI::NonMI, "delete 2"); + session->addCommand(MI::NonMI, "delete 2"); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->rowCount(), 0); @@ -738,12 +741,12 @@ { TestDebugSession *session = new TestDebugSession; - QSignalSpy showStepInSourceSpy(session, SIGNAL(showStepInSource(QUrl,int,QString))); + QSignalSpy showStepInSourceSpy(session, &TestDebugSession::showStepInSource); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 29); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); @@ -776,7 +779,7 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); @@ -817,7 +820,7 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->frameStackModel()->fetchFramesCalled, 1); @@ -887,7 +890,7 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 21); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); @@ -916,7 +919,7 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->rowCount(), 4); @@ -982,10 +985,10 @@ TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; - cfg.config().writeEntry(GDBDebugger::remoteGdbRunEntry, QUrl::fromLocalFile(findSourceFile("gdb_script_empty"))); - QVERIFY(session->startProgram(&cfg, m_iface)); + cfg.config().writeEntry(remoteGdbRunEntry, QUrl::fromLocalFile(findSourceFile("gdb_script_empty"))); + QVERIFY(session->startDebugging(&cfg, m_iface)); - session->addCommand(GDBMI::NonMI, QString("attach %0").arg(debugeeProcess.pid())); + session->addCommand(MI::NonMI, QString("attach %0").arg(debugeeProcess.pid())); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); @@ -1038,7 +1041,7 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 22); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); @@ -1065,7 +1068,7 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); @@ -1111,7 +1114,7 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add("ts"); @@ -1154,7 +1157,7 @@ const QString quotedTestString("\"" + testString + "\""); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(quotedTestString); //just a constant string @@ -1192,7 +1195,7 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add("ts"); @@ -1216,7 +1219,7 @@ //start a second debug session session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(300); @@ -1248,7 +1251,7 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); session->stopDebugger(); @@ -1264,14 +1267,14 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); session->run(); @@ -1287,7 +1290,7 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); @@ -1320,7 +1323,7 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(500); @@ -1366,7 +1369,7 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 23); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 23); @@ -1388,15 +1391,15 @@ TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 24); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->currentFrame(), 0); stackModel->setCurrentFrame(1); QCOMPARE(stackModel->currentFrame(), 1); QTest::qWait(500); QCOMPARE(stackModel->currentFrame(), 1); - session->slotUserGDBCmd("print x"); + session->addUserCommand("print x"); QTest::qWait(500); //currentFrame must not reset to 0; Bug 222882 QCOMPARE(stackModel->currentFrame(), 1); @@ -1410,7 +1413,7 @@ TestLaunchConfiguration cfg(findExecutable("debugeeslow")); QString fileName = findSourceFile("debugeeslow.cpp"); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::ActiveState); QTest::qWait(2000); @@ -1428,7 +1431,7 @@ TestLaunchConfiguration cfg(findExecutable("debugeeqt")); breakpoints()->addCodeBreakpoint("main"); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); for(int i=0; i<20; i++) { session->stepInto(); } @@ -1444,8 +1447,8 @@ TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint("main"); - QVERIFY(session->startProgram(&cfg, m_iface)); - session->addCommand(GDBMI::NonMI, "break debugee.cpp:32"); + QVERIFY(session->startDebugging(&cfg, m_iface)); + session->addCommand(MI::NonMI, "break debugee.cpp:32"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); //wait for breakpoints update @@ -1471,10 +1474,10 @@ TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); - grp.writeEntry(GDBDebugger::remoteGdbConfigEntry, QUrl::fromLocalFile(configScript.fileName())); + grp.writeEntry(remoteGdbConfigEntry, QUrl::fromLocalFile(configScript.fileName())); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile("debugee.cpp"), 31); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); @@ -1492,12 +1495,12 @@ TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); - grp.writeEntry(GDBDebugger::remoteGdbConfigEntry, QUrl::fromLocalFile(configScript.fileName())); + grp.writeEntry(remoteGdbConfigEntry, QUrl::fromLocalFile(configScript.fileName())); for (int i = 0; i < 2; ++i) { TestDebugSession* session = new TestDebugSession; - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::EndedState); } @@ -1518,9 +1521,9 @@ TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); - grp.writeEntry(GDBDebugger::remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); + grp.writeEntry(remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); @@ -1556,10 +1559,10 @@ TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); - grp.writeEntry(GDBDebugger::remoteGdbShellEntry, QUrl::fromLocalFile((shellScript.fileName()+"-copy"))); - grp.writeEntry(GDBDebugger::remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); + grp.writeEntry(remoteGdbShellEntry, QUrl::fromLocalFile((shellScript.fileName()+"-copy"))); + grp.writeEntry(remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); @@ -1600,10 +1603,10 @@ TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); - grp.writeEntry(GDBDebugger::remoteGdbShellEntry, QUrl::fromLocalFile(shellScript.fileName()+"-copy")); - grp.writeEntry(GDBDebugger::remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); + grp.writeEntry(remoteGdbShellEntry, QUrl::fromLocalFile(shellScript.fileName()+"-copy")); + grp.writeEntry(remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); @@ -1651,10 +1654,10 @@ TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); - grp.writeEntry(GDBDebugger::remoteGdbShellEntry, QUrl::fromLocalFile((shellScript.fileName()+"-copy"))); - grp.writeEntry(GDBDebugger::remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); + grp.writeEntry(remoteGdbShellEntry, QUrl::fromLocalFile((shellScript.fileName()+"-copy"))); + grp.writeEntry(remoteGdbRunEntry, QUrl::fromLocalFile(runScript.fileName())); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); @@ -1672,7 +1675,7 @@ //************************** second session session = new TestDebugSession; - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); @@ -1702,7 +1705,7 @@ KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 20); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 20); session->run(); @@ -1721,7 +1724,7 @@ KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 31); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 29); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); @@ -1741,14 +1744,14 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("debugeeexception.cpp")), 29); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); TestFrameStackModel* fsModel = session->frameStackModel(); QCOMPARE(fsModel->currentFrame(), 0); QCOMPARE(session->line(), 29); - session->addCommand(new GDBCommand(GDBMI::NonMI, "catch throw")); + session->addCommand(new MI::MICommand(MI::NonMI, "catch throw")); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); @@ -1775,15 +1778,15 @@ QString fileName = findSourceFile("debugeethreads.cpp"); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 38); - QVERIFY(session->startProgram(&cfg, m_iface)); + QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(1000); - QSignalSpy outputSpy(session, SIGNAL(gdbUserCommandStdout(QString))); + QSignalSpy outputSpy(session, &TestDebugSession::debuggerUserCommandOutput); session->addCommand( - new UserCommand(GDBMI::ThreadInfo,"")); - session->addCommand(new UserCommand(GDBMI::StackListLocals, QLatin1String("0"))); + new MI::UserCommand(MI::ThreadInfo,"")); + session->addCommand(new MI::UserCommand(MI::StackListLocals, QLatin1String("0"))); QTest::qWait(1000); QCOMPARE(outputSpy.count(), 2); QVERIFY(outputSpy.last().at(0).toString().contains(QLatin1String("--thread 1"))); @@ -1794,7 +1797,7 @@ void GdbTest::parseBug304730() { - FileSymbol file; + MI::FileSymbol file; file.contents = QByteArray("^done,bkpt={" "number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"\",times=\"0\"," "original-location=\"/media/portable/Projects/BDSInpainting/PatchMatch/PatchMatch.hpp:231\"}," @@ -1814,9 +1817,9 @@ "file=\"/media/portable/Projects/BDSInpainting/Drivers/../PatchMatch/PatchMatch.hpp\"," "fullname=\"/media/portable/Projects/BDSInpainting/PatchMatch/PatchMatch.hpp\",line=\"231\"}"); - MIParser parser; + MI::MIParser parser; - std::unique_ptr record(parser.parse(&file)); + std::unique_ptr record(parser.parse(&file)); QVERIFY(record.get() != nullptr); } @@ -1830,7 +1833,7 @@ //TODO check if the additional location breakpoint is added - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->line(), 19); @@ -1851,7 +1854,7 @@ breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add("argc"); @@ -1869,7 +1872,7 @@ session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QTest::qWait(300); @@ -1890,7 +1893,7 @@ //there'll be about 3-4 breakpoints, but we treat it like one. TestLaunchConfiguration c(findExecutable("debugeemultiplebreakpoint")); KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint("debugeemultiplebreakpoint.cpp:52"); - session->startProgram(&c, m_iface); + session->startDebugging(&c, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); @@ -1905,15 +1908,15 @@ TestLaunchConfiguration c(findExecutable("debugeemultilocbreakpoint")); breakpoints()->addCodeBreakpoint("main"); - session->startProgram(&c, m_iface); + session->startDebugging(&c, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); - session->addCommand(new GDBCommand(GDBMI::NonMI, "rbreak .*aPl.*B")); + session->addCommand(new MI::MICommand(MI::NonMI, "rbreak .*aPl.*B")); QTest::qWait(100); session->run(); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 3); - session->addCommand(new GDBCommand(GDBMI::BreakDelete, "")); + session->addCommand(new MI::MICommand(MI::BreakDelete, "")); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } @@ -1924,7 +1927,7 @@ TestLaunchConfiguration c(findExecutable("debugeeslow")); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint("debugeeslow.cpp:25"); - session->startProgram(&c, m_iface); + session->startDebugging(&c, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QVERIFY(session->currentLine() >= 24 && session->currentLine() <= 26 ); @@ -1962,7 +1965,7 @@ KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(debugeeFileName), 28); - session->startProgram(&cfg, m_iface); + session->startDebugging(&cfg, m_iface); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->stepInto(); @@ -1982,22 +1985,22 @@ TestLaunchConfiguration c(debugee, KIO::upUrl(debugee)); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint("spacedebugee.cpp:30"); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); - session->startProgram(&c, m_iface); + session->startDebugging(&c, m_iface); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); #endif } -bool GdbTest::waitForState(GDBDebugger::DebugSession *session, DebugSession::DebuggerState state, +bool GdbTest::waitForState(DebugSession *session, DebugSession::DebuggerState state, const char *file, int line, bool waitForIdle) { - QPointer s(session); //session can get deleted in DebugController + QPointer s(session); //session can get deleted in DebugController QTime stopWatch; stopWatch.start(); - while (s.data()->state() != state || (waitForIdle && s->stateIsOn(s_dbgBusy))) { + while (s.data()->state() != state || (waitForIdle && s->debuggerStateIsOn(s_dbgBusy))) { if (stopWatch.elapsed() > 5000) { qWarning() << "current state" << s.data()->state() << "waiting for" << state; QTest::qFail(qPrintable(QString("Timeout before reaching state %0").arg(state)), @@ -2022,10 +2025,10 @@ qDebug() << "Reached state " << state << " in " << file << ':' << line; return true; } +} // end of namespace GDB +} // end of namespace KDevMI -} - -QTEST_MAIN(GDBDebugger::GdbTest) +QTEST_MAIN(KDevMI::GDB::GdbTest) #include "test_gdb.moc" diff --git a/debuggers/gdb/variablecontroller.h b/debuggers/gdb/variablecontroller.h --- a/debuggers/gdb/variablecontroller.h +++ b/debuggers/gdb/variablecontroller.h @@ -1,77 +1,45 @@ /* - * GDB Debugger Support + * GDB-specific variable controller implementation + * Copyright 2016 Aetf * - * Copyright 2007 Hamish Rodda - * Copyright 2008 Vladimir Prus - * Copyright 2009 Niko Sams - * - * 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 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ -#ifndef GDBDEBUGGER_VARIABLECONTROLLER_H -#define GDBDEBUGGER_VARIABLECONTROLLER_H - -#include +#ifndef VARIABLECONTROLLER_H +#define VARIABLECONTROLLER_H -#include "gdbglobal.h" +#include "mivariablecontroller.h" -using namespace KDevelop; +namespace KDevMI { namespace GDB { -namespace GDBMI { -struct AsyncRecord; -struct ResultRecord; -struct Value; -} - -namespace GDBDebugger { - -class GDBController; class DebugSession; - -class VariableController : public KDevelop::IVariableController +class VariableController : public MIVariableController { Q_OBJECT public: VariableController(DebugSession* parent); - Variable* createVariable(TreeModel* model, TreeItem* parent, - const QString& expression, - const QString& display = "") override; - KTextEditor::Range expressionRangeUnderCursor(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor) override; - void addWatch(KDevelop::Variable* variable) override; - void addWatchpoint(KDevelop::Variable* variable) override; - void update() override; - -private slots: - void programStopped(const GDBMI::AsyncRecord &r); - void stateChanged(KDevelop::IDebugSession::DebuggerState); - private: DebugSession* debugSession() const; - - void updateLocals(); - - void handleVarUpdate(const GDBMI::ResultRecord& r); - void addWatch(const GDBMI::ResultRecord& r); - void addWatchpoint(const GDBMI::ResultRecord& r); - - void handleEvent(IDebugSession::event_t event) override; }; -} +} // end of namespace GDB +} // end of namespace KDevMI -#endif // GDBDEBUGGER_VARIABLECONTROLLER_H +#endif // VARIABLECONTROLLER_H diff --git a/debuggers/gdb/variablecontroller.cpp b/debuggers/gdb/variablecontroller.cpp --- a/debuggers/gdb/variablecontroller.cpp +++ b/debuggers/gdb/variablecontroller.cpp @@ -1,256 +1,37 @@ /* - * GDB Debugger Support + * GDB-specific variable controller implementation + * Copyright 2016 Aetf * - * Copyright 2007 Hamish Rodda - * Copyright 2008 Vladimir Prus - * Copyright 2009 Niko Sams - * - * 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 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, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ #include "variablecontroller.h" -#include -#include -#include -#include -#include - -#include "gdbcommand.h" #include "debugsession.h" -#include "stringhelpers.h" -#include "gdbvariable.h" -#include "debug.h" - -#include -using namespace GDBDebugger; -using namespace KDevelop; +using namespace KDevMI::GDB; -VariableController::VariableController(DebugSession* parent) - : KDevelop::IVariableController(parent) +VariableController::VariableController(DebugSession *parent) + : MIVariableController(parent) { - Q_ASSERT(parent); - connect(parent, &DebugSession::programStopped, this, &VariableController::programStopped); - connect(parent, &DebugSession::stateChanged, this, &VariableController::stateChanged); } DebugSession *VariableController::debugSession() const { return static_cast(const_cast(QObject::parent())); } - -void VariableController::programStopped(const GDBMI::AsyncRecord& r) -{ - if (debugSession()->stateIsOn(s_shuttingDown)) return; - - if (r.hasField("reason") && r["reason"].literal() == "function-finished" - && r.hasField("gdb-result-var")) - { - variableCollection()->watches()->addFinishResult(r["gdb-result-var"].literal()); - } else { - variableCollection()->watches()->removeFinishResult(); - } -} - -void VariableController::update() -{ - qCDebug(DEBUGGERGDB) << autoUpdate(); - if (autoUpdate() & UpdateWatches) { - variableCollection()->watches()->reinstall(); - } - - if (autoUpdate() & UpdateLocals) { - updateLocals(); - } - - if ((autoUpdate() & UpdateLocals) || - ((autoUpdate() & UpdateWatches) && variableCollection()->watches()->childCount() > 0)) - { - debugSession()->addCommand( - new GDBCommand(GDBMI::VarUpdate, "--all-values *", this, - &VariableController::handleVarUpdate)); - } -} - -void VariableController::handleVarUpdate(const GDBMI::ResultRecord& r) -{ - const GDBMI::Value& changed = r["changelist"]; - for (int i = 0; i < changed.size(); ++i) - { - const GDBMI::Value& var = changed[i]; - GdbVariable* v = GdbVariable::findByVarobjName(var["name"].literal()); - // v can be NULL here if we've already removed locals after step, - // but the corresponding -var-delete command is still in the queue. - if (v) { - v->handleUpdate(var); - } - } -} -class StackListArgumentsHandler : public GDBCommandHandler -{ -public: - StackListArgumentsHandler(QStringList localsName) - : m_localsName(localsName) - {} - - void handle(const GDBMI::ResultRecord &r) override - { - if (!KDevelop::ICore::self()->debugController()) return; //happens on shutdown - - // FIXME: handle error. - const GDBMI::Value& locals = r["stack-args"][0]["args"]; - - for (int i = 0; i < locals.size(); i++) { - m_localsName << locals[i].literal(); - } - QList variables = KDevelop::ICore::self()->debugController()->variableCollection() - ->locals()->updateLocals(m_localsName); - foreach (Variable *v, variables) { - v->attachMaybe(); - } - } - -private: - QStringList m_localsName; -}; - -class StackListLocalsHandler : public GDBCommandHandler -{ -public: - StackListLocalsHandler(DebugSession *session) - : m_session(session) - {} - - void handle(const GDBMI::ResultRecord &r) override - { - // FIXME: handle error. - - const GDBMI::Value& locals = r["locals"]; - - QStringList localsName; - for (int i = 0; i < locals.size(); i++) { - const GDBMI::Value& var = locals[i]; - localsName << var["name"].literal(); - } - int frame = m_session->frameStackModel()->currentFrame(); - m_session->addCommand( //dont'show value, low-frame, high-frame - new GDBCommand(GDBMI::StackListArguments, QString("0 %1 %2").arg(frame).arg(frame), - new StackListArgumentsHandler(localsName))); - } - -private: - DebugSession *m_session; -}; - -void VariableController::updateLocals() -{ - debugSession()->addCommand( - new GDBCommand(GDBMI::StackListLocals, "--simple-values", - new StackListLocalsHandler(debugSession()))); -} - -KTextEditor::Range VariableController::expressionRangeUnderCursor(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor) -{ - QString line = doc->line(cursor.line()); - int index = cursor.column(); - QChar c = line[index]; - if (!c.isLetterOrNumber() && c != '_') - return {}; - - int start = Utils::expressionAt(line, index+1); - int end = index; - for (; end < line.size(); ++end) - { - QChar c = line[end]; - if (!(c.isLetterOrNumber() || c == '_')) - break; - } - if (!(start < end)) - return {}; - - return { KTextEditor::Cursor{cursor.line(), start}, KTextEditor::Cursor{cursor.line(), end} }; -} - - -void VariableController::addWatch(KDevelop::Variable* variable) -{ - // FIXME: should add async 'get full expression' method - // to GdbVariable, not poke at varobj. In that case, - // we will be able to make addWatch a generic method, not - // gdb-specific one. - if (GdbVariable *gv = dynamic_cast(variable)) - { - debugSession()->addCommand( - new GDBCommand(GDBMI::VarInfoPathExpression, - gv->varobj(), - this, - &VariableController::addWatch)); - } -} - -void VariableController::addWatchpoint(KDevelop::Variable* variable) -{ - // FIXME: should add async 'get full expression' method - // to GdbVariable, not poke at varobj. In that case, - // we will be able to make addWatchpoint a generic method, not - // gdb-specific one. - if (GdbVariable *gv = dynamic_cast(variable)) - { - debugSession()->addCommand( - new GDBCommand(GDBMI::VarInfoPathExpression, - gv->varobj(), - this, - &VariableController::addWatchpoint)); - } -} - -void VariableController::addWatch(const GDBMI::ResultRecord& r) -{ - // FIXME: handle error. - if (r.reason == "done" && !r["path_expr"].literal().isEmpty()) { - variableCollection()->watches()->add(r["path_expr"].literal()); - } -} - -void VariableController::addWatchpoint(const GDBMI::ResultRecord& r) -{ - if (r.reason == "done" && !r["path_expr"].literal().isEmpty()) { - KDevelop::ICore::self()->debugController()->breakpointModel()->addWatchpoint(r["path_expr"].literal()); - } -} - -KDevelop::Variable* VariableController:: -createVariable(TreeModel* model, TreeItem* parent, - const QString& expression, const QString& display) -{ - return new GdbVariable(model, parent, expression, display); -} - -void VariableController::handleEvent(IDebugSession::event_t event) -{ - IVariableController::handleEvent(event); -} - -void VariableController::stateChanged(IDebugSession::DebuggerState state) -{ - if (state == IDebugSession::EndedState) { - GdbVariable::markAllDead(); - } -} - - -