summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger/breakpoint.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'languages/cpp/debugger/breakpoint.cpp')
-rw-r--r--languages/cpp/debugger/breakpoint.cpp719
1 files changed, 719 insertions, 0 deletions
diff --git a/languages/cpp/debugger/breakpoint.cpp b/languages/cpp/debugger/breakpoint.cpp
new file mode 100644
index 00000000..7bcf674f
--- /dev/null
+++ b/languages/cpp/debugger/breakpoint.cpp
@@ -0,0 +1,719 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 by John Birch
+ email : jbb@kdevelop.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "breakpoint.h"
+#include "gdbcontroller.h"
+#include "gdbcommand.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qfileinfo.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+#include <stdio.h>
+#include <typeinfo>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+static int BPKey_ = 0;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Breakpoint::Breakpoint(bool temporary, bool enabled)
+ : s_pending_(true),
+ s_actionAdd_(true),
+ s_actionClear_(false),
+ s_actionModify_(false),
+ s_actionDie_(false),
+ s_dbgProcessing_(false),
+ s_enabled_(enabled),
+ s_temporary_(temporary),
+ s_hardwareBP_(false),
+ s_tracingEnabled_(false),
+ s_traceFormatStringEnabled_(false),
+
+ dbgId_(-1),
+ hits_(0),
+ key_(BPKey_++),
+ active_(-1),
+ ignoreCount_(0),
+ condition_("")
+{
+}
+
+/***************************************************************************/
+
+Breakpoint::~Breakpoint()
+{
+}
+
+void Breakpoint::sendToGdb(GDBController* controller)
+{
+ // Need to issue 'modifyBreakpoint' when setting breakpoint is done
+ controller_ = controller;
+
+ // FIXME: should either make sure this widget is disabled
+ // when needed, or implement simular logic.
+ if (controller->stateIsOn(s_dbgNotStarted))
+ {
+ // Can't modify breakpoint now, will try again later.
+ setPending(true);
+ return;
+ }
+
+ setPending(false);
+
+ bool restart = false;
+ // FIXME: this will only catch command for which gdb
+ // produces the "^running" marker.
+ // FIXME: this probably won't work if there are other
+ // run commands in the thread already.
+ if (controller->stateIsOn(s_appRunning)
+ && !controller->stateIsOn(s_explicitBreakInto))
+ {
+ kdDebug(9012) << "PAUSING APP\n";
+ controller->pauseApp();
+ restart = true;
+ }
+
+ if (isActionAdd())
+ {
+ // This prevents us from sending breakpoint command to
+ // gdb for empty breakpoints, when user haven't even
+ // typed function name, or address, or variable.
+ //
+ // Check for isDbgProcessing makes sure we don't issue
+ // several -break-insert commands before getting
+ // output from the first one.
+ if (isValid() && !isDbgProcessing())
+ {
+ setBreakpoint(controller);
+ }
+ }
+ else
+ {
+ if (isActionClear())
+ {
+ clearBreakpoint(controller);
+ }
+ else
+ {
+ if (isActionModify())
+ {
+ modifyBreakpoint(controller);
+ }
+ }
+ }
+
+ if (restart) {
+ kdDebug(9012) << "RESTARING APP\n";
+ GDBCommand *cmd = new GDBCommand("-exec-continue");
+ cmd->setRun(true);
+ controller->addCommand(cmd);
+ }
+}
+
+void Breakpoint::clearBreakpoint(GDBController* /*c*/)
+{
+ controller()->addCommandBeforeRun(
+ new GDBCommand(dbgRemoveCommand(),
+ this,
+ &Breakpoint::handleDeleted));
+}
+
+void Breakpoint::applicationExited(GDBController*)
+{
+}
+
+
+void Breakpoint::setBreakpoint(GDBController* controller)
+{
+ setDbgProcessing(true);
+
+ // Don't use handler mechanism yet, because the reply
+ // should contain internal id of breakpoint (not gdb id), so that we
+ // can match gdb id with the breakpoint instance we've set.
+
+ // Note that at startup we issue several breakpoint commands, so can't
+ // just store last breakpoint. Need to stack of last breakpoint commands,
+ // but that for later.
+ //
+ // When this command is finished, slotParseGDBBreakpointSet
+ // will be called by the controller.
+ controller->addCommandBeforeRun(
+ new GDBCommand(dbgSetCommand(controller),
+ this,
+ &Breakpoint::handleSet, true));
+}
+
+
+void Breakpoint::modifyBreakpoint(GDBController* controller)
+{
+ controller->
+ addCommandBeforeRun(
+ new ModifyBreakpointCommand(QString("-break-condition %1 ") +
+ conditional(), this));
+ controller->
+ addCommandBeforeRun(
+ new ModifyBreakpointCommand(QString("-break-after %1 ") +
+ QString::number(ignoreCount()), this));
+
+ controller->
+ addCommandBeforeRun(
+ new ModifyBreakpointCommand(isEnabled() ?
+ QString("-break-enable %1")
+ : QString("-break-disable %1"), this));
+}
+
+void Breakpoint::removedInGdb()
+{
+ setActionDie();
+ emit modified(this);
+}
+
+
+bool Breakpoint::match(const Breakpoint* breakpoint) const
+{
+ // simple case
+ if (this == breakpoint)
+ return true;
+
+ // Type case
+ if (typeid(*this) != typeid(*breakpoint))
+ return false;
+
+ return match_data(breakpoint);
+}
+
+/***************************************************************************/
+
+QString Breakpoint::dbgRemoveCommand() const
+{
+ if (dbgId_>0)
+ return QString("-break-delete %1").arg(dbgId_); // gdb command - not translatable
+
+ return QString();
+}
+
+
+/***************************************************************************/
+
+// called when debugger ends
+void Breakpoint::reset()
+{
+ dbgId_ = -1;
+ s_pending_ = true;
+ s_actionAdd_ = true; // waiting for the debugger to start
+ s_actionClear_ = false;
+ // All breakpoint properties will be automatically sent to
+ // gdb when breakpoint is first added, no matter what value
+ // this field has.
+ s_actionModify_ = false;
+ s_dbgProcessing_ = false;
+ s_hardwareBP_ = false;
+ hits_ = 0;
+ active_ = -1;
+}
+
+/***************************************************************************/
+
+void Breakpoint::setActive(int active, int id)
+{
+ active_ = active;
+ dbgId_ = id;
+
+ if (s_pending_ && !(s_actionAdd_ && s_actionModify_)) {
+ s_pending_ = false;
+ s_actionModify_ = false;
+ }
+
+ s_actionAdd_ = false;
+ s_actionClear_ = false;
+ s_actionDie_ = false;
+ s_dbgProcessing_ = false;
+}
+
+/***************************************************************************/
+
+QString Breakpoint::statusDisplay(int activeFlag) const
+{
+ QString status="";
+ if (!s_enabled_)
+ status = i18n("Disabled");
+ else
+ if (s_pending_)
+ {
+ if (s_actionAdd_)
+ status = i18n("Pending (add)");
+ if (s_actionClear_)
+ status = i18n("Pending (clear)");
+ if (s_actionModify_)
+ status = i18n("Pending (modify)");
+ }
+ else
+ if (isActive(activeFlag))
+ status = i18n("Active");
+
+ return status;
+}
+
+QString Breakpoint::traceRealFormatString() const
+{
+ QString result;
+
+ if (traceFormatStringEnabled())
+ {
+ result = traceFormatString();
+ }
+ else
+ {
+ result = "Tracepoint";
+ if (const FilePosBreakpoint* fb
+ = dynamic_cast<const FilePosBreakpoint*>(this))
+ {
+ result += " at " + fb->location() + ": ";
+ }
+ else
+ {
+ result += " " + QString::number(key()) + ": ";
+ }
+ for(QStringList::const_iterator i = tracedExpressions_.begin(),
+ e = tracedExpressions_.end(); i != e; ++i)
+ {
+ result += " " + *i + " = %d";
+ }
+ }
+
+ // Quote the thing
+ result = "\"" + result + "\\n\"";
+
+ for(QStringList::const_iterator i = tracedExpressions_.begin(),
+ e = tracedExpressions_.end(); i != e; ++i)
+ {
+ result += ", " + *i;
+ }
+
+ return result;
+}
+
+void Breakpoint::handleSet(const GDBMI::ResultRecord& r)
+{
+ // Try to find gdb id. It's a bit harder that it should be,
+ // because field names differ depending on breakpoint type.
+
+ int id = -1;
+
+ if (r.hasField("bkpt"))
+ id = r["bkpt"]["number"].literal().toInt();
+ else if (r.hasField("wpt"))
+ id = r["wpt"]["number"].literal().toInt();
+ else if (r.hasField("hw-rwpt"))
+ id = r["hw-rwpt"]["number"].literal().toInt();
+ // We don't have access watchpoints in UI yet, but
+ // for future.
+ else if (r.hasField("hw-awpt"))
+ id = r["hw-awpt"]["number"].literal().toInt();
+
+ if (id == -1)
+ {
+ // If can't set because file not found yet,
+ // will need to try later.
+ setPending(true);
+ }
+ else
+ {
+ setActive(0 /* unused m_activeFlag */, id);
+ }
+
+ // Need to do this so that if breakpoint is not set
+ // (because the file is not found)
+ // we unset isDbgProcessing flag, so that breakpoint can
+ // be set on next stop.
+ setDbgProcessing(false);
+
+ // Immediately call modifyBreakpoint to set all breakpoint
+ // properties, such as condition.
+ modifyBreakpoint(controller_);
+
+ emit modified(this);
+}
+
+void Breakpoint::handleDeleted(const GDBMI::ResultRecord& /*r*/)
+{
+ kdDebug(9012) << "inside handleDeleted\n";
+ setActionDie();
+ if (FilePosBreakpoint* fp = dynamic_cast<FilePosBreakpoint*>(this))
+ {
+ kdDebug(9012) << "handleDeleted, line is " << fp->lineNum() << "\n";
+ }
+ emit modified(this);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+FilePosBreakpoint::FilePosBreakpoint()
+: subtype_(filepos),
+ line_(-1)
+{}
+
+FilePosBreakpoint::FilePosBreakpoint(const QString &fileName, int lineNum,
+ bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled)
+{
+ // Sets 'subtype'
+ setLocation(QString("%1:%2").arg(fileName).arg(lineNum));
+}
+
+FilePosBreakpoint::~FilePosBreakpoint()
+{
+}
+
+QString FilePosBreakpoint::dbgSetCommand(GDBController *c) const
+{
+ QString cmdStr = "-break-insert";
+
+ if (isTemporary())
+ cmdStr = cmdStr + " -t";
+
+ if (c->miPendingBreakpoints())
+ cmdStr = cmdStr + " -f";
+
+ return cmdStr + " " + location_;
+}
+
+bool FilePosBreakpoint::match_data(const Breakpoint *xb) const
+{
+ const FilePosBreakpoint* b = static_cast<const FilePosBreakpoint*>(xb);
+
+ if (b)
+ return location_ == b->location_;
+ else
+ return false;
+}
+
+QString FilePosBreakpoint::displayType() const
+{
+ return i18n("Code breakpoint", "Code");
+}
+
+bool FilePosBreakpoint::isValid() const
+{
+ return !location_.isEmpty();
+}
+
+bool FilePosBreakpoint::hasFileAndLine() const
+{
+ return line_ != -1;
+}
+
+QString FilePosBreakpoint::fileName() const
+{
+ return fileName_;
+}
+
+unsigned FilePosBreakpoint::lineNum() const
+{
+ return line_;
+}
+
+
+
+QString FilePosBreakpoint::location(bool compact) const
+{
+ if (subtype_ == filepos && hasFileAndLine() && compact)
+ {
+ return QFileInfo(fileName_).fileName()+":"+QString::number(line_);
+ }
+ else
+ {
+ return location_;
+ }
+}
+
+/***************************************************************************/
+
+void FilePosBreakpoint::setLocation(const QString& location)
+{
+ location_ = location;
+
+ QRegExp regExp1("(.*):(\\d+)$");
+ regExp1.setMinimal(true);
+ if ( regExp1.search(location, 0) >= 0 )
+ {
+ subtype_ = filepos;
+
+ QString t = regExp1.cap(1);
+ QString dirPath = QFileInfo(t).dirPath();
+ if ( dirPath == "." )
+ {
+ QString existingDirPath = QFileInfo(fileName_).dirPath();
+ if (existingDirPath != ".")
+ fileName_ = existingDirPath+"/"+regExp1.cap(1);
+ else
+ fileName_ = regExp1.cap(1);
+ }
+ else
+ fileName_ = regExp1.cap(1);
+
+ line_ = regExp1.cap(2).toInt();
+
+ location_ = QString("%1:%2").arg(fileName_).arg(regExp1.cap(2));
+ }
+ else
+ {
+ // Could be address as well, but it's treated absolutely
+ // the same everywhere.
+ subtype_ = function;
+ }
+}
+
+void FilePosBreakpoint::handleSet(const GDBMI::ResultRecord& r)
+{
+ // Below logic gets filename and line from gdb response, and
+ // allows us to show breakpoint marker even for function
+ // breakpoints. Unfortunately, 'fullname' field is available only in
+ // post-6.4 versions of gdb and if we try to use 'file', then
+ // KDevelop won't be able to find that file to show the marker.
+ if (r.hasField("bkpt"))
+ {
+ const GDBMI::Value& v = r["bkpt"];
+ if (v.hasField("fullname") && v.hasField("line"))
+ {
+ fileName_ = v["fullname"].literal();
+ line_ = v["line"].literal().toInt();
+ }
+ }
+
+ Breakpoint::handleSet(r);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+Watchpoint::Watchpoint(const QString& varName, bool temporary, bool enabled)
+ : Breakpoint(temporary, enabled),
+ varName_(varName)
+{
+}
+
+/***************************************************************************/
+
+Watchpoint::~Watchpoint()
+{
+}
+
+void Watchpoint::setBreakpoint(GDBController* controller)
+{
+ if (isEnabled())
+ {
+ setDbgProcessing(true);
+
+ controller->addCommandBeforeRun(
+ new GDBCommand(
+ QString("-data-evaluate-expression &%1").arg(varName_),
+ this,
+ &Watchpoint::handleAddressComputed));
+ }
+}
+
+void Watchpoint::handleAddressComputed(const GDBMI::ResultRecord& r)
+{
+ address_ = r["value"].literal().toULongLong(0, 16);
+ controller()->addCommandBeforeRun(
+ new GDBCommand(
+ QString("-break-watch *%1").arg(r["value"].literal()),
+ static_cast<Breakpoint*>(this),
+ &Watchpoint::handleSet));
+}
+
+void Watchpoint::applicationExited(GDBController* c)
+{
+ if (!c->stateIsOn(s_dbgNotStarted))
+ {
+ // Note: not using 'clearBreakpoint' as it will delete breakpoint
+ // completely.
+
+ controller()->addCommand(
+ new GDBCommand(dbgRemoveCommand()));
+ setDbgId(-1);
+ setEnabled(false);
+ setActionAdd(true);
+ address_ = static_cast<unsigned long long>(-1);
+ emit modified(this);
+ }
+}
+
+void Watchpoint::removedInGdb()
+{
+ // Do nothing. Watchpoints must be preserved
+ // even if they are gone in gdb.
+}
+
+/***************************************************************************/
+
+QString Watchpoint::dbgSetCommand(GDBController *) const
+{
+ return QString("-break-watch ")+varName_; // gdb command - not translatable
+}
+
+/***************************************************************************/
+
+bool Watchpoint::match_data(const Breakpoint* xb) const
+{
+ const Watchpoint* b = static_cast<const Watchpoint*>(xb);
+
+ return (varName_ == b->varName_);
+}
+
+ReadWatchpoint::ReadWatchpoint(const QString& varName, bool temporary, bool enabled)
+ : Watchpoint(varName, temporary, enabled)
+{
+}
+
+QString ReadWatchpoint::dbgSetCommand(GDBController *) const
+{
+ return QString("-break-watch -r ")+varName();
+}
+
+bool ReadWatchpoint::match_data(const Breakpoint* xb) const
+{
+ const ReadWatchpoint* b = static_cast<const ReadWatchpoint*>(xb);
+
+ return (varName() == b->varName());
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+//ExitBreakpoint::ExitBreakpoint(bool temporary, bool enabled) :
+// Breakpoint(temporary, enabled)
+//{
+//}
+//
+///***************************************************************************/
+//
+//ExitBreakpoint::~ExitBreakpoint()
+//{
+//}
+//
+///***************************************************************************/
+//
+//QString ExitBreakpoint::dbgSetCommand() const
+//{
+// return "";
+//}
+//
+///***************************************************************************/
+//
+//bool ExitBreakpoint::match(const Breakpoint* brkpt) const
+//{
+// // simple case
+// if (this == brkpt)
+// return true;
+//
+// // Type case
+// const ExitBreakpoint* check = dynamic_cast<const ExitBreakpoint*>(brkpt);
+// if (!check)
+// return false;
+//
+// // member case
+// return true;
+//}
+//
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+//
+// These are implemented in gdb but can cause a lot of breakpoints
+// to be set. This needs more thought before being implemented
+
+//RegExpBreakpoint::RegExpBreakpoint(bool temporary, bool enabled) :
+// Breakpoint(temporary, enabled)
+//{
+//}
+//
+///***************************************************************************/
+//
+//RegExpBreakpoint::~RegExpBreakpoint()
+//{
+//}
+//
+///***************************************************************************/
+//
+//QString RegExpBreakpoint::dbgSetCommand() const
+//{
+// return "";
+//}
+//
+///***************************************************************************/
+//
+////QString RegExpBreakpoint::dbgRemoveCommand() const
+////{
+//// return "";
+////}
+//
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+// Most catch options arn't implemented in gdb so ignore this for now.
+
+//CatchBreakpoint::CatchBreakpoint(bool temporary, bool enabled) :
+// Breakpoint(temporary, enabled)
+//{
+//}
+//
+///***************************************************************************/
+//
+//CatchBreakpoint::~CatchBreakpoint()
+//{
+//}
+//
+///***************************************************************************/
+//
+//QString CatchBreakpoint::dbgSetCommand() const
+//{
+// return "";
+//}
+//
+///***************************************************************************/
+//
+////QString CatchBreakpoint::dbgRemoveCommand() const
+////{
+//// return "";
+////}
+//
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+}
+
+#include "breakpoint.moc"