File indexing completed on 2024-04-28 04:38:33
0001 /* 0002 SPDX-FileCopyrightText: 2002 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> 0003 SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org> 0004 SPDX-FileCopyrightText: 2006, 2008 Vladimir Prus <ghost@cs.msu.su> 0005 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org> 0006 SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "mibreakpointcontroller.h" 0012 0013 #include "debuglog.h" 0014 #include "midebugsession.h" 0015 #include "mi/micommand.h" 0016 #include "stringhelpers.h" 0017 0018 #include <debugger/breakpoint/breakpoint.h> 0019 #include <debugger/breakpoint/breakpointmodel.h> 0020 0021 #include <KLocalizedString> 0022 0023 using namespace KDevMI; 0024 using namespace KDevMI::MI; 0025 using namespace KDevelop; 0026 0027 struct MIBreakpointController::Handler : public MICommandHandler 0028 { 0029 Handler(MIBreakpointController* controller, const BreakpointDataPtr& b, 0030 BreakpointModel::ColumnFlags columns) 0031 : controller(controller) 0032 , breakpoint(b) 0033 , columns(columns) 0034 { 0035 breakpoint->sent |= columns; 0036 breakpoint->dirty &= ~columns; 0037 } 0038 0039 void handle(const ResultRecord& r) override 0040 { 0041 breakpoint->sent &= ~columns; 0042 0043 if (r.reason == QLatin1String("error")) { 0044 breakpoint->errors |= columns; 0045 0046 int row = controller->breakpointRow(breakpoint); 0047 if (row >= 0) { 0048 controller->updateErrorText(row, r[QStringLiteral("msg")].literal()); 0049 qCWarning(DEBUGGERCOMMON) << r[QStringLiteral("msg")].literal(); 0050 } 0051 } else { 0052 if (breakpoint->errors & columns) { 0053 breakpoint->errors &= ~columns; 0054 0055 if (breakpoint->errors) { 0056 // Since at least one error column cleared, it's possible that any remaining 0057 // error bits were collateral damage; try resending the corresponding columns 0058 // to see whether errors remain. 0059 breakpoint->dirty |= (breakpoint->errors & ~breakpoint->sent); 0060 } 0061 } 0062 } 0063 } 0064 0065 bool handlesError() override 0066 { 0067 return true; 0068 } 0069 0070 MIBreakpointController* controller; 0071 BreakpointDataPtr breakpoint; 0072 BreakpointModel::ColumnFlags columns; 0073 }; 0074 0075 struct MIBreakpointController::UpdateHandler : public MIBreakpointController::Handler 0076 { 0077 UpdateHandler(MIBreakpointController* c, const BreakpointDataPtr& b, 0078 BreakpointModel::ColumnFlags columns) 0079 : Handler(c, b, columns) {} 0080 0081 void handle(const ResultRecord &r) override 0082 { 0083 Handler::handle(r); 0084 0085 int row = controller->breakpointRow(breakpoint); 0086 if (row >= 0) { 0087 // Note: send further updates even if we got an error; who knows: perhaps 0088 // these additional updates will "unstuck" the error condition. 0089 if (breakpoint->sent == 0 && breakpoint->dirty != 0) { 0090 controller->sendUpdates(row); 0091 } 0092 controller->recalculateState(row); 0093 } 0094 } 0095 }; 0096 0097 struct MIBreakpointController::InsertedHandler : public MIBreakpointController::Handler 0098 { 0099 InsertedHandler(MIBreakpointController* c, const BreakpointDataPtr& b, 0100 BreakpointModel::ColumnFlags columns) 0101 : Handler(c, b, columns) {} 0102 0103 void handle(const ResultRecord &r) override 0104 { 0105 Handler::handle(r); 0106 0107 int row = controller->breakpointRow(breakpoint); 0108 0109 if (r.reason != QLatin1String("error")) { 0110 QString bkptKind; 0111 for (auto& kind : {QStringLiteral("bkpt"), QStringLiteral("wpt"), QStringLiteral("hw-rwpt"), QStringLiteral("hw-awpt")}) { 0112 if (r.hasField(kind)) { 0113 bkptKind = kind; 0114 break; 0115 } 0116 } 0117 if (bkptKind.isEmpty()) { 0118 qCWarning(DEBUGGERCOMMON) << "Gdb sent unknown breakpoint kind"; 0119 return; 0120 } 0121 0122 const Value& miBkpt = r[bkptKind]; 0123 0124 breakpoint->debuggerId = miBkpt[QStringLiteral("number")].toInt(); 0125 0126 if (row >= 0) { 0127 controller->updateFromDebugger(row, miBkpt); 0128 if (breakpoint->dirty != 0) 0129 controller->sendUpdates(row); 0130 } else { 0131 // breakpoint was deleted while insertion was in flight 0132 controller->debugSession()->addCommand(BreakDelete, 0133 QString::number(breakpoint->debuggerId), 0134 CmdImmediately); 0135 } 0136 } 0137 0138 if (row >= 0) { 0139 controller->recalculateState(row); 0140 } 0141 } 0142 }; 0143 0144 struct MIBreakpointController::DeleteHandler : MIBreakpointController::Handler { 0145 DeleteHandler(MIBreakpointController* c, const BreakpointDataPtr& b) 0146 : Handler(c, b, BreakpointModel::ColumnFlags()) {} 0147 0148 void handle(const ResultRecord&) override 0149 { 0150 controller->m_pendingDeleted.removeAll(breakpoint); 0151 } 0152 }; 0153 0154 struct MIBreakpointController::IgnoreChanges { 0155 explicit IgnoreChanges(MIBreakpointController& controller) 0156 : controller(controller) 0157 { 0158 ++controller.m_ignoreChanges; 0159 } 0160 0161 ~IgnoreChanges() 0162 { 0163 --controller.m_ignoreChanges; 0164 } 0165 0166 MIBreakpointController& controller; 0167 0168 private: 0169 Q_DISABLE_COPY(IgnoreChanges) 0170 }; 0171 0172 MIBreakpointController::MIBreakpointController(MIDebugSession * parent) 0173 : IBreakpointController(parent) 0174 { 0175 Q_ASSERT(parent); 0176 connect(parent, &MIDebugSession::inferiorStopped, 0177 this, &MIBreakpointController::programStopped); 0178 0179 int numBreakpoints = breakpointModel()->breakpoints().size(); 0180 for (int row = 0; row < numBreakpoints; ++row) 0181 breakpointAdded(row); 0182 } 0183 0184 MIDebugSession *MIBreakpointController::debugSession() const 0185 { 0186 Q_ASSERT(QObject::parent()); 0187 return static_cast<MIDebugSession *>(const_cast<QObject*>(QObject::parent())); 0188 } 0189 0190 int MIBreakpointController::breakpointRow(const BreakpointDataPtr& breakpoint) 0191 { 0192 return m_breakpoints.indexOf(breakpoint); 0193 } 0194 0195 void MIBreakpointController::setDeleteDuplicateBreakpoints(bool enable) 0196 { 0197 m_deleteDuplicateBreakpoints = enable; 0198 } 0199 0200 void MIBreakpointController::initSendBreakpoints() 0201 { 0202 for (int row = 0; row < m_breakpoints.size(); ++row) { 0203 BreakpointDataPtr breakpoint = m_breakpoints[row]; 0204 if (breakpoint->debuggerId < 0 && breakpoint->sent == 0) { 0205 createBreakpoint(row); 0206 } 0207 } 0208 } 0209 0210 void MIBreakpointController::breakpointAdded(int row) 0211 { 0212 if (m_ignoreChanges > 0) 0213 return; 0214 0215 auto breakpoint = BreakpointDataPtr::create(); 0216 m_breakpoints.insert(row, breakpoint); 0217 0218 const Breakpoint* modelBreakpoint = breakpointModel()->breakpoint(row); 0219 if (!modelBreakpoint->enabled()) 0220 breakpoint->dirty |= BreakpointModel::EnableColumnFlag; 0221 if (!modelBreakpoint->condition().isEmpty()) 0222 breakpoint->dirty |= BreakpointModel::ConditionColumnFlag; 0223 if (modelBreakpoint->ignoreHits() != 0) 0224 breakpoint->dirty |= BreakpointModel::IgnoreHitsColumnFlag; 0225 if (!modelBreakpoint->address().isEmpty()) 0226 breakpoint->dirty |= BreakpointModel::LocationColumnFlag; 0227 0228 createBreakpoint(row); 0229 } 0230 0231 void MIBreakpointController::breakpointModelChanged(int row, BreakpointModel::ColumnFlags columns) 0232 { 0233 if (m_ignoreChanges > 0) 0234 return; 0235 0236 BreakpointDataPtr breakpoint = m_breakpoints.at(row); 0237 breakpoint->dirty |= columns & 0238 (BreakpointModel::EnableColumnFlag | BreakpointModel::LocationColumnFlag | 0239 BreakpointModel::ConditionColumnFlag | BreakpointModel::IgnoreHitsColumnFlag); 0240 0241 if (breakpoint->sent != 0) { 0242 // Throttle the amount of commands we send to GDB; the response handler of currently 0243 // in-flight commands will take care of sending the update. 0244 // This also prevents us from sending updates while a break-create command is in-flight. 0245 return; 0246 } 0247 0248 if (breakpoint->debuggerId < 0) { 0249 createBreakpoint(row); 0250 } else { 0251 sendUpdates(row); 0252 } 0253 } 0254 0255 void MIBreakpointController::breakpointAboutToBeDeleted(int row) 0256 { 0257 if (m_ignoreChanges > 0) 0258 return; 0259 0260 BreakpointDataPtr breakpoint = m_breakpoints.at(row); 0261 m_breakpoints.removeAt(row); 0262 0263 if (breakpoint->debuggerId < 0) { 0264 // Two possibilities: 0265 // (1) Breakpoint has never been sent to GDB, so we're done 0266 // (2) Breakpoint has been sent to GDB, but we haven't received 0267 // the response yet; the response handler will delete the 0268 // breakpoint. 0269 return; 0270 } 0271 0272 if (debugSession()->debuggerStateIsOn(s_dbgNotStarted)) 0273 return; 0274 0275 debugSession()->addCommand( 0276 BreakDelete, QString::number(breakpoint->debuggerId), 0277 new DeleteHandler(this, breakpoint), CmdImmediately); 0278 m_pendingDeleted << breakpoint; 0279 } 0280 0281 // Note: despite the name, this is in fact session state changed. 0282 void MIBreakpointController::debuggerStateChanged(IDebugSession::DebuggerState state) 0283 { 0284 IgnoreChanges ignoreChanges(*this); 0285 if (state == IDebugSession::EndedState || 0286 state == IDebugSession::NotStartedState) { 0287 for (int row = 0; row < m_breakpoints.size(); ++row) { 0288 updateState(row, Breakpoint::NotStartedState); 0289 } 0290 } else if (state == IDebugSession::StartingState) { 0291 for (int row = 0; row < m_breakpoints.size(); ++row) { 0292 updateState(row, Breakpoint::DirtyState); 0293 } 0294 } 0295 } 0296 0297 void MIBreakpointController::createBreakpoint(int row) 0298 { 0299 if (debugSession()->debuggerStateIsOn(s_dbgNotStarted)) 0300 return; 0301 0302 BreakpointDataPtr breakpoint = m_breakpoints.at(row); 0303 Breakpoint* modelBreakpoint = breakpointModel()->breakpoint(row); 0304 0305 Q_ASSERT(breakpoint->debuggerId < 0 && breakpoint->sent == 0); 0306 0307 if (modelBreakpoint->location().isEmpty()) 0308 return; 0309 0310 if (modelBreakpoint->kind() == Breakpoint::CodeBreakpoint) { 0311 QString location; 0312 if (modelBreakpoint->line() != -1) { 0313 location = QStringLiteral("%0:%1") 0314 .arg(modelBreakpoint->url().url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash)) 0315 .arg(modelBreakpoint->line() + 1); 0316 } else { 0317 location = modelBreakpoint->location(); 0318 } 0319 0320 if (location == QLatin1String("catch throw")) { 0321 location = QStringLiteral("exception throw"); 0322 } 0323 0324 // Note: We rely on '-f' to be automatically added by the MICommand logic 0325 QString arguments; 0326 if (!modelBreakpoint->enabled()) 0327 arguments += QLatin1String("-d "); 0328 if (!modelBreakpoint->condition().isEmpty()) 0329 arguments += QStringLiteral("-c %0 ").arg(Utils::quoteExpression(modelBreakpoint->condition())); 0330 if (modelBreakpoint->ignoreHits() != 0) 0331 arguments += QStringLiteral("-i %0 ").arg(modelBreakpoint->ignoreHits()); 0332 arguments += Utils::quoteExpression(location); 0333 0334 BreakpointModel::ColumnFlags sent = 0335 BreakpointModel::EnableColumnFlag | 0336 BreakpointModel::ConditionColumnFlag | 0337 BreakpointModel::IgnoreHitsColumnFlag | 0338 BreakpointModel::LocationColumnFlag; 0339 debugSession()->addCommand(BreakInsert, arguments, 0340 new InsertedHandler(this, breakpoint, sent), 0341 CmdImmediately); 0342 } else { 0343 QString opt; 0344 if (modelBreakpoint->kind() == Breakpoint::ReadBreakpoint) 0345 opt = QStringLiteral("-r "); 0346 else if (modelBreakpoint->kind() == Breakpoint::AccessBreakpoint) 0347 opt = QStringLiteral("-a "); 0348 0349 debugSession()->addCommand(BreakWatch, 0350 opt + Utils::quoteExpression(modelBreakpoint->location()), 0351 new InsertedHandler(this, breakpoint, 0352 BreakpointModel::LocationColumnFlag), 0353 CmdImmediately); 0354 } 0355 0356 recalculateState(row); 0357 } 0358 0359 void MIBreakpointController::sendUpdates(int row) 0360 { 0361 if (debugSession()->debuggerStateIsOn(s_dbgNotStarted)) 0362 return; 0363 0364 BreakpointDataPtr breakpoint = m_breakpoints.at(row); 0365 Breakpoint* modelBreakpoint = breakpointModel()->breakpoint(row); 0366 0367 Q_ASSERT(breakpoint->debuggerId >= 0 && breakpoint->sent == 0); 0368 0369 if (breakpoint->dirty & BreakpointModel::LocationColumnFlag) { 0370 // Gdb considers locations as fixed, so delete and re-create the breakpoint 0371 debugSession()->addCommand(BreakDelete, 0372 QString::number(breakpoint->debuggerId), CmdImmediately); 0373 breakpoint->debuggerId = -1; 0374 createBreakpoint(row); 0375 return; 0376 } 0377 0378 if (breakpoint->dirty & BreakpointModel::EnableColumnFlag) { 0379 debugSession()->addCommand(modelBreakpoint->enabled() ? BreakEnable : BreakDisable, 0380 QString::number(breakpoint->debuggerId), 0381 new UpdateHandler(this, breakpoint, 0382 BreakpointModel::EnableColumnFlag), 0383 CmdImmediately); 0384 } 0385 if (breakpoint->dirty & BreakpointModel::IgnoreHitsColumnFlag) { 0386 debugSession()->addCommand(BreakAfter, 0387 QStringLiteral("%0 %1").arg(breakpoint->debuggerId) 0388 .arg(modelBreakpoint->ignoreHits()), 0389 new UpdateHandler(this, breakpoint, 0390 BreakpointModel::IgnoreHitsColumnFlag), 0391 CmdImmediately); 0392 } 0393 if (breakpoint->dirty & BreakpointModel::ConditionColumnFlag) { 0394 debugSession()->addCommand(BreakCondition, 0395 QStringLiteral("%0 %1").arg(breakpoint->debuggerId) 0396 .arg(modelBreakpoint->condition()), 0397 new UpdateHandler(this, breakpoint, 0398 BreakpointModel::ConditionColumnFlag), 0399 CmdImmediately); 0400 } 0401 0402 recalculateState(row); 0403 } 0404 0405 void MIBreakpointController::recalculateState(int row) 0406 { 0407 BreakpointDataPtr breakpoint = m_breakpoints.at(row); 0408 0409 if (breakpoint->errors == 0) 0410 updateErrorText(row, QString()); 0411 0412 Breakpoint::BreakpointState newState = Breakpoint::NotStartedState; 0413 if (debugSession()->state() != IDebugSession::EndedState && 0414 debugSession()->state() != IDebugSession::NotStartedState) { 0415 if (!debugSession()->debuggerStateIsOn(s_dbgNotStarted)) { 0416 if (breakpoint->dirty == 0 && breakpoint->sent == 0) { 0417 if (breakpoint->pending) { 0418 newState = Breakpoint::PendingState; 0419 } else { 0420 newState = Breakpoint::CleanState; 0421 } 0422 } else { 0423 newState = Breakpoint::DirtyState; 0424 } 0425 } 0426 } 0427 0428 updateState(row, newState); 0429 } 0430 0431 int MIBreakpointController::rowFromDebuggerId(int gdbId) const 0432 { 0433 for (int row = 0; row < m_breakpoints.size(); ++row) { 0434 if (gdbId == m_breakpoints[row]->debuggerId) 0435 return row; 0436 } 0437 return -1; 0438 } 0439 0440 void MIBreakpointController::notifyBreakpointCreated(const AsyncRecord& r) 0441 { 0442 const Value& miBkpt = r[QStringLiteral("bkpt")]; 0443 0444 // Breakpoints with multiple locations are represented by a parent breakpoint (e.g. 1) 0445 // and multiple child breakpoints (e.g. 1.1, 1.2, 1.3, ...). 0446 // We ignore the child breakpoints here in the current implementation; this can lead to dubious 0447 // results in the UI when breakpoints are marked in document views (e.g. when a breakpoint 0448 // applies to multiple overloads of a C++ function simultaneously) and in disassembly 0449 // (e.g. when a breakpoint is set in an inlined functions). 0450 if (miBkpt[QStringLiteral("number")].literal().contains(QLatin1Char('.'))) 0451 return; 0452 0453 createFromDebugger(miBkpt); 0454 } 0455 0456 void MIBreakpointController::notifyBreakpointModified(const AsyncRecord& r) 0457 { 0458 const Value& miBkpt = r[QStringLiteral("bkpt")]; 0459 const int gdbId = miBkpt[QStringLiteral("number")].toInt(); 0460 const int row = rowFromDebuggerId(gdbId); 0461 0462 if (row < 0) { 0463 for (const auto& breakpoint : qAsConst(m_pendingDeleted)) { 0464 if (breakpoint->debuggerId == gdbId) { 0465 // Received a modification of a breakpoint whose deletion is currently 0466 // in-flight; simply ignore it. 0467 return; 0468 } 0469 } 0470 0471 qCWarning(DEBUGGERCOMMON) << "Received a modification of an unknown breakpoint"; 0472 createFromDebugger(miBkpt); 0473 } else { 0474 updateFromDebugger(row, miBkpt); 0475 } 0476 } 0477 0478 void MIBreakpointController::notifyBreakpointDeleted(const AsyncRecord& r) 0479 { 0480 const int gdbId = r[QStringLiteral("id")].toInt(); 0481 const int row = rowFromDebuggerId(gdbId); 0482 0483 if (row < 0) { 0484 // The user may also have deleted the breakpoint via the UI simultaneously 0485 return; 0486 } 0487 0488 IgnoreChanges ignoreChanges(*this); 0489 breakpointModel()->removeRow(row); 0490 m_breakpoints.removeAt(row); 0491 } 0492 0493 void MIBreakpointController::createFromDebugger(const Value& miBkpt) 0494 { 0495 const QString type = miBkpt[QStringLiteral("type")].literal(); 0496 Breakpoint::BreakpointKind gdbKind; 0497 if (type == QLatin1String("breakpoint") || type == QLatin1String("catchpoint")) { 0498 gdbKind = Breakpoint::CodeBreakpoint; 0499 } else if (type == QLatin1String("watchpoint") || type == QLatin1String("hw watchpoint")) { 0500 gdbKind = Breakpoint::WriteBreakpoint; 0501 } else if (type == QLatin1String("read watchpoint")) { 0502 gdbKind = Breakpoint::ReadBreakpoint; 0503 } else if (type == QLatin1String("acc watchpoint")) { 0504 gdbKind = Breakpoint::AccessBreakpoint; 0505 } else { 0506 qCWarning(DEBUGGERCOMMON) << "Unknown breakpoint type " << type; 0507 return; 0508 } 0509 0510 // During debugger startup, we want to avoid creating duplicate breakpoints when the same breakpoint 0511 // appears both in our model and in a init file e.g. .gdbinit 0512 BreakpointModel* model = breakpointModel(); 0513 const int numRows = model->rowCount(); 0514 for (int row = 0; row < numRows; ++row) { 0515 BreakpointDataPtr breakpoint = m_breakpoints.at(row); 0516 const bool breakpointSent = breakpoint->debuggerId >= 0 || breakpoint->sent != 0; 0517 if (breakpointSent && !m_deleteDuplicateBreakpoints) 0518 continue; 0519 0520 Breakpoint* modelBreakpoint = model->breakpoint(row); 0521 if (modelBreakpoint->kind() != gdbKind) 0522 continue; 0523 0524 if (gdbKind == Breakpoint::CodeBreakpoint) { 0525 bool sameLocation = false; 0526 0527 if (miBkpt.hasField(QStringLiteral("fullname")) && miBkpt.hasField(QStringLiteral("line"))) { 0528 const QString location = Utils::unquoteExpression(miBkpt[QStringLiteral("fullname")].literal()); 0529 const int line = miBkpt[QStringLiteral("line")].toInt() - 1; 0530 if (location == modelBreakpoint->url().url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash) && 0531 line == modelBreakpoint->line()) 0532 { 0533 sameLocation = true; 0534 } 0535 } 0536 0537 if (!sameLocation && miBkpt.hasField(QStringLiteral("original-location"))) { 0538 const QString location = miBkpt[QStringLiteral("original-location")].literal(); 0539 if (location == modelBreakpoint->location()) { 0540 sameLocation = true; 0541 } else { 0542 QRegExp rx(QStringLiteral("^(.+):(\\d+)$")); 0543 if (rx.indexIn(location) != -1 && 0544 Utils::unquoteExpression(rx.cap(1)) == modelBreakpoint->url().url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash) && 0545 rx.cap(2).toInt() - 1 == modelBreakpoint->line()) { 0546 sameLocation = true; 0547 } 0548 } 0549 } 0550 0551 if (!sameLocation && miBkpt.hasField(QStringLiteral("what")) && miBkpt[QStringLiteral("what")].literal() == QLatin1String("exception throw")) { 0552 if (modelBreakpoint->expression() == QLatin1String("catch throw") || 0553 modelBreakpoint->expression() == QLatin1String("exception throw")) { 0554 sameLocation = true; 0555 } 0556 } 0557 0558 if (!sameLocation) 0559 continue; 0560 } else { 0561 if (Utils::unquoteExpression(miBkpt[QStringLiteral("original-location")].literal()) != modelBreakpoint->expression()) { 0562 continue; 0563 } 0564 } 0565 0566 QString condition; 0567 if (miBkpt.hasField(QStringLiteral("cond"))) { 0568 condition = miBkpt[QStringLiteral("cond")].literal(); 0569 } 0570 if (condition != modelBreakpoint->condition()) 0571 continue; 0572 0573 // Breakpoint is equivalent 0574 if (!breakpointSent) { 0575 breakpoint->debuggerId = miBkpt[QStringLiteral("number")].toInt(); 0576 0577 // Reasonable people can probably have different opinions about what the "correct" behavior 0578 // should be for the "enabled" and "ignore hits" column. 0579 // Here, we let the status in KDevelop's UI take precedence, which we suspect to be 0580 // marginally more useful. Dirty data will be sent during the initial sending of the 0581 // breakpoint list. 0582 const bool gdbEnabled = miBkpt[QStringLiteral("enabled")].literal() == QLatin1String("y"); 0583 if (gdbEnabled != modelBreakpoint->enabled()) 0584 breakpoint->dirty |= BreakpointModel::EnableColumnFlag; 0585 0586 int gdbIgnoreHits = 0; 0587 if (miBkpt.hasField(QStringLiteral("ignore"))) 0588 gdbIgnoreHits = miBkpt[QStringLiteral("ignore")].toInt(); 0589 if (gdbIgnoreHits != modelBreakpoint->ignoreHits()) 0590 breakpoint->dirty |= BreakpointModel::IgnoreHitsColumnFlag; 0591 0592 updateFromDebugger(row, miBkpt, BreakpointModel::EnableColumnFlag | BreakpointModel::IgnoreHitsColumnFlag); 0593 return; 0594 } 0595 0596 // Breakpoint from the model has already been sent, but we want to delete duplicates 0597 // It is not entirely clear _which_ breakpoint ought to be deleted, and reasonable people 0598 // may have different opinions. 0599 // We suspect that it is marginally more useful to delete the existing model breakpoint; 0600 // after all, this only happens when a user command creates a breakpoint, and perhaps the 0601 // user intends to modify the breakpoint they created manually. In any case, 0602 // this situation should only happen rarely (in particular, when a user sets a breakpoint 0603 // inside the remote run script). 0604 model->removeRows(row, 1); 0605 break; // fall through to pick up the manually created breakpoint in the model 0606 } 0607 0608 // No equivalent breakpoint found, or we have one but want to be consistent with GDB's 0609 // behavior of allowing multiple equivalent breakpoint. 0610 IgnoreChanges ignoreChanges(*this); 0611 const int row = m_breakpoints.size(); 0612 Q_ASSERT(row == model->rowCount()); 0613 0614 switch (gdbKind) { 0615 case Breakpoint::WriteBreakpoint: model->addWatchpoint(); break; 0616 case Breakpoint::ReadBreakpoint: model->addReadWatchpoint(); break; 0617 case Breakpoint::AccessBreakpoint: model->addAccessWatchpoint(); break; 0618 case Breakpoint::CodeBreakpoint: model->addCodeBreakpoint(); break; 0619 default: Q_ASSERT(false); return; 0620 } 0621 0622 // Since we are in ignore-changes mode, we have to add the BreakpointData manually. 0623 auto breakpoint = BreakpointDataPtr::create(); 0624 m_breakpoints << breakpoint; 0625 breakpoint->debuggerId = miBkpt[QStringLiteral("number")].toInt(); 0626 0627 updateFromDebugger(row, miBkpt); 0628 } 0629 0630 // This method is required for the legacy interface which will be removed 0631 void MIBreakpointController::sendMaybe(KDevelop::Breakpoint*) 0632 { 0633 Q_ASSERT(false); 0634 } 0635 0636 void MIBreakpointController::updateFromDebugger(int row, const Value& miBkpt, BreakpointModel::ColumnFlags lockedColumns) 0637 { 0638 IgnoreChanges ignoreChanges(*this); 0639 BreakpointDataPtr breakpoint = m_breakpoints[row]; 0640 Breakpoint* modelBreakpoint = breakpointModel()->breakpoint(row); 0641 0642 // Commands that are currently in flight will overwrite the modification we have received, 0643 // so do not update the corresponding data 0644 lockedColumns |= breakpoint->sent | breakpoint->dirty; 0645 0646 // TODO: 0647 // Gdb has a notion of "original-location", which is the "expression" or "location" used 0648 // to set the breakpoint, and notions of the actual location of the breakpoint (function name, 0649 // address, source file and line). The breakpoint model currently does not map well to this 0650 // (though it arguably should), and does not support multi-location breakpoints at all. 0651 // We try to do the best we can until the breakpoint model gets cleaned up. 0652 if (miBkpt.hasField(QStringLiteral("fullname")) && miBkpt.hasField(QStringLiteral("line"))) { 0653 modelBreakpoint->setLocation( 0654 QUrl::fromLocalFile(Utils::unquoteExpression(miBkpt[QStringLiteral("fullname")].literal())), 0655 miBkpt[QStringLiteral("line")].toInt() - 1); 0656 } else if (miBkpt.hasField(QStringLiteral("original-location"))) { 0657 QRegExp rx(QStringLiteral("^(.+):(\\d+)$")); 0658 QString location = miBkpt[QStringLiteral("original-location")].literal(); 0659 if (rx.indexIn(location) != -1) { 0660 modelBreakpoint->setLocation(QUrl::fromLocalFile(Utils::unquoteExpression(rx.cap(1))), 0661 rx.cap(2).toInt()-1); 0662 } else { 0663 modelBreakpoint->setData(Breakpoint::LocationColumn, Utils::unquoteExpression(location)); 0664 } 0665 } else if (miBkpt.hasField(QStringLiteral("what"))) { 0666 modelBreakpoint->setExpression(miBkpt[QStringLiteral("what")].literal()); 0667 } else { 0668 qCWarning(DEBUGGERCOMMON) << "Breakpoint doesn't contain required location/expression data"; 0669 } 0670 0671 if (!(lockedColumns & BreakpointModel::EnableColumnFlag)) { 0672 bool enabled = true; 0673 if (miBkpt.hasField(QStringLiteral("enabled"))) { 0674 if (miBkpt[QStringLiteral("enabled")].literal() == QLatin1String("n")) 0675 enabled = false; 0676 } 0677 modelBreakpoint->setData(Breakpoint::EnableColumn, enabled ? Qt::Checked : Qt::Unchecked); 0678 breakpoint->dirty &= ~BreakpointModel::EnableColumnFlag; 0679 } 0680 0681 if (!(lockedColumns & BreakpointModel::ConditionColumnFlag)) { 0682 QString condition; 0683 if (miBkpt.hasField(QStringLiteral("cond"))) { 0684 condition = miBkpt[QStringLiteral("cond")].literal(); 0685 } 0686 modelBreakpoint->setCondition(condition); 0687 breakpoint->dirty &= ~BreakpointModel::ConditionColumnFlag; 0688 } 0689 0690 if (!(lockedColumns & BreakpointModel::IgnoreHitsColumnFlag)) { 0691 int ignoreHits = 0; 0692 if (miBkpt.hasField(QStringLiteral("ignore"))) { 0693 ignoreHits = miBkpt[QStringLiteral("ignore")].toInt(); 0694 } 0695 modelBreakpoint->setIgnoreHits(ignoreHits); 0696 breakpoint->dirty &= ~BreakpointModel::IgnoreHitsColumnFlag; 0697 } 0698 0699 breakpoint->pending = false; 0700 if (miBkpt.hasField(QStringLiteral("addr")) && miBkpt[QStringLiteral("addr")].literal() == QLatin1String("<PENDING>")) { 0701 breakpoint->pending = true; 0702 } 0703 0704 int hitCount = 0; 0705 if (miBkpt.hasField(QStringLiteral("times"))) { 0706 hitCount = miBkpt[QStringLiteral("times")].toInt(); 0707 } 0708 updateHitCount(row, hitCount); 0709 0710 recalculateState(row); 0711 } 0712 0713 void MIBreakpointController::programStopped(const AsyncRecord& r) 0714 { 0715 if (!r.hasField(QStringLiteral("reason"))) 0716 return; 0717 0718 const QString reason = r[QStringLiteral("reason")].literal(); 0719 0720 int debuggerId = -1; 0721 if (reason == QLatin1String("breakpoint-hit")) { 0722 debuggerId = r[QStringLiteral("bkptno")].toInt(); 0723 } else if (reason == QLatin1String("watchpoint-trigger")) { 0724 debuggerId = r[QStringLiteral("wpt")][QStringLiteral("number")].toInt(); 0725 } else if (reason == QLatin1String("read-watchpoint-trigger")) { 0726 debuggerId = r[QStringLiteral("hw-rwpt")][QStringLiteral("number")].toInt(); 0727 } else if (reason == QLatin1String("access-watchpoint-trigger")) { 0728 debuggerId = r[QStringLiteral("hw-awpt")][QStringLiteral("number")].toInt(); 0729 } 0730 0731 if (debuggerId < 0) 0732 return; 0733 0734 int row = rowFromDebuggerId(debuggerId); 0735 if (row < 0) 0736 return; 0737 0738 QString msg; 0739 if (r.hasField(QStringLiteral("value"))) { 0740 if (r[QStringLiteral("value")].hasField(QStringLiteral("old"))) { 0741 msg += i18n("<br>Old value: %1", r[QStringLiteral("value")][QStringLiteral("old")].literal()); 0742 } 0743 if (r[QStringLiteral("value")].hasField(QStringLiteral("new"))) { 0744 msg += i18n("<br>New value: %1", r[QStringLiteral("value")][QStringLiteral("new")].literal()); 0745 } 0746 } 0747 0748 notifyHit(row, msg); 0749 } 0750 0751 #include "moc_mibreakpointcontroller.cpp"