/*************************************************************************** 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 #include #include #include #include #include #include #include #include /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ 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(TQString("-break-condition %1 ") + conditional(), this)); controller-> addCommandBeforeRun( new ModifyBreakpointCommand(TQString("-break-after %1 ") + TQString::number(ignoreCount()), this)); controller-> addCommandBeforeRun( new ModifyBreakpointCommand(isEnabled() ? TQString("-break-enable %1") : TQString("-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); } /***************************************************************************/ TQString Breakpoint::dbgRemoveCommand() const { if (dbgId_>0) return TQString("-break-delete %1").arg(dbgId_); // gdb command - not translatable return TQString(); } /***************************************************************************/ // 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; } /***************************************************************************/ TQString Breakpoint::statusDisplay(int activeFlag) const { TQString 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; } TQString Breakpoint::traceRealFormatString() const { TQString result; if (traceFormatStringEnabled()) { result = traceFormatString(); } else { result = "Tracepoint"; if (const FilePosBreakpoint* fb = dynamic_cast(this)) { result += " at " + fb->location() + ": "; } else { result += " " + TQString::number(key()) + ": "; } for(TQStringList::const_iterator i = tracedExpressions_.begin(), e = tracedExpressions_.end(); i != e; ++i) { result += " " + *i + " = %d"; } } // Quote the thing result = "\"" + result + "\\n\""; for(TQStringList::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(this)) { kdDebug(9012) << "handleDeleted, line is " << fp->lineNum() << "\n"; } emit modified(this); } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ FilePosBreakpoint::FilePosBreakpoint() : subtype_(filepos), line_(-1) {} FilePosBreakpoint::FilePosBreakpoint(const TQString &fileName, int lineNum, bool temporary, bool enabled) : Breakpoint(temporary, enabled) { // Sets 'subtype' setLocation(TQString("%1:%2").arg(fileName).arg(lineNum)); } FilePosBreakpoint::~FilePosBreakpoint() { } TQString FilePosBreakpoint::dbgSetCommand(GDBController *c) const { TQString 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(xb); if (b) return location_ == b->location_; else return false; } TQString FilePosBreakpoint::displayType() const { return i18n("Code breakpoint", "Code"); } bool FilePosBreakpoint::isValid() const { return !location_.isEmpty(); } bool FilePosBreakpoint::hasFileAndLine() const { return line_ != -1; } TQString FilePosBreakpoint::fileName() const { return fileName_; } unsigned FilePosBreakpoint::lineNum() const { return line_; } TQString FilePosBreakpoint::location(bool compact) const { if (subtype_ == filepos && hasFileAndLine() && compact) { return TQFileInfo(fileName_).fileName()+":"+TQString::number(line_); } else { return location_; } } /***************************************************************************/ void FilePosBreakpoint::setLocation(const TQString& location) { location_ = location; TQRegExp regExp1("(.*):(\\d+)$"); regExp1.setMinimal(true); if ( regExp1.search(location, 0) >= 0 ) { subtype_ = filepos; TQString t = regExp1.cap(1); TQString dirPath = TQFileInfo(t).dirPath(); if ( dirPath == "." ) { TQString existingDirPath = TQFileInfo(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_ = TQString("%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 TQString& 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( TQString("-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( TQString("-break-watch *%1").arg(r["value"].literal()), static_cast(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(-1); emit modified(this); } } void Watchpoint::removedInGdb() { // Do nothing. Watchpoints must be preserved // even if they are gone in gdb. } /***************************************************************************/ TQString Watchpoint::dbgSetCommand(GDBController *) const { return TQString("-break-watch ")+varName_; // gdb command - not translatable } /***************************************************************************/ bool Watchpoint::match_data(const Breakpoint* xb) const { const Watchpoint* b = static_cast(xb); return (varName_ == b->varName_); } ReadWatchpoint::ReadWatchpoint(const TQString& varName, bool temporary, bool enabled) : Watchpoint(varName, temporary, enabled) { } TQString ReadWatchpoint::dbgSetCommand(GDBController *) const { return TQString("-break-watch -r ")+varName(); } bool ReadWatchpoint::match_data(const Breakpoint* xb) const { const ReadWatchpoint* b = static_cast(xb); return (varName() == b->varName()); } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ //ExitBreakpoint::ExitBreakpoint(bool temporary, bool enabled) : // Breakpoint(temporary, enabled) //{ //} // ///***************************************************************************/ // //ExitBreakpoint::~ExitBreakpoint() //{ //} // ///***************************************************************************/ // //TQString 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(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() //{ //} // ///***************************************************************************/ // //TQString RegExpBreakpoint::dbgSetCommand() const //{ // return ""; //} // ///***************************************************************************/ // ////TQString 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() //{ //} // ///***************************************************************************/ // //TQString CatchBreakpoint::dbgSetCommand() const //{ // return ""; //} // ///***************************************************************************/ // ////TQString CatchBreakpoint::dbgRemoveCommand() const ////{ //// return ""; ////} // /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ } #include "breakpoint.moc"