summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>2011-02-03 16:26:23 +0100
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>2011-02-03 16:26:23 +0100
commit91ead6c81807b75ee23669be904c1519af2cc8fa (patch)
tree43c612a60a1ec23321169f0cc45404d55e9b06bb /src
parentd5a33d2860eaecc6466fc7d194d4f34f91861d58 (diff)
downloadqt-creator-91ead6c81807b75ee23669be904c1519af2cc8fa.tar.gz
Debugger[CDB]: Refactor breakpoint handling.
Add a command to list breakpoints enabling id access. Implemented breakpoint handling similar to gdb using breakpoint ids (no longer delete and re-set all breakpoints on a change). Save the module that is reported back in the session so that it can be re-used for the next start. Keep a per-debugger-session cache of fileName->Module for adding breakpoints to accelerate setting breakpoints in the same file. Polish the breakpoint tooltip.
Diffstat (limited to 'src')
-rw-r--r--src/libs/qtcreatorcdbext/eventcallback.cpp14
-rw-r--r--src/libs/qtcreatorcdbext/extensioncontext.cpp11
-rw-r--r--src/libs/qtcreatorcdbext/gdbmihelpers.cpp19
-rw-r--r--src/libs/qtcreatorcdbext/gdbmihelpers.h2
-rw-r--r--src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp6
-rw-r--r--src/plugins/debugger/breakhandler.cpp96
-rw-r--r--src/plugins/debugger/breakhandler.h2
-rw-r--r--src/plugins/debugger/breakwindow.cpp6
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp222
-rw-r--r--src/plugins/debugger/cdb/cdbengine.h11
-rw-r--r--src/plugins/debugger/cdb/cdbparsehelpers.cpp72
-rw-r--r--src/plugins/debugger/cdb/cdbparsehelpers.h8
12 files changed, 327 insertions, 142 deletions
diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp
index d04ad11a57..f5f8d36e95 100644
--- a/src/libs/qtcreatorcdbext/eventcallback.cpp
+++ b/src/libs/qtcreatorcdbext/eventcallback.cpp
@@ -107,16 +107,14 @@ STDMETHODIMP EventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT b)
typedef ExtensionContext::StopReasonMap StopReasonMap;
typedef ExtensionContext::StopReasonMap::value_type StopReasonMapValue;
- ULONG id = 0;
+ ULONG uId = 0;
ULONG64 address = 0;
- b->GetId(&id);
- b->GetOffset(&address);
-
StopReasonMap stopReason;
- stopReason.insert(StopReasonMapValue(std::string("breakpointId"), toString(id)));
- if (address)
+ if (SUCCEEDED(b->GetId(&uId)))
+ stopReason.insert(StopReasonMapValue(std::string("breakpointId"), toString(uId)));
+ if (SUCCEEDED(b->GetOffset(&address)))
stopReason.insert(StopReasonMapValue(std::string("breakpointAddress"), toString(address)));
- ExtensionContext::instance().setStopReason(stopReason, "breakpoint");
+ ExtensionContext::instance().setStopReason(stopReason, ExtensionContext::breakPointStopReasonC);
return m_wrapped ? m_wrapped->Breakpoint(b) : S_OK;
}
@@ -172,7 +170,7 @@ STDMETHODIMP EventCallback::Exception(
std::ostringstream str;
formatGdbmiHash(str, parameters);
- ExtensionContext::instance().setStopReason(parameters, ExtensionContext::breakPointStopReasonC);
+ ExtensionContext::instance().setStopReason(parameters, "exception");
ExtensionContext::instance().report('E', 0, 0, "exception", "%s", str.str().c_str());
return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK;
}
diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp
index 965aac037a..cbae841539 100644
--- a/src/libs/qtcreatorcdbext/extensioncontext.cpp
+++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp
@@ -198,17 +198,6 @@ void ExtensionContext::notifyIdleCommand(CIDebugClient *client)
} else {
str << ",stack=" << stackInfo;
}
- // Report breakpoints
- const StopReasonMap::const_iterator rit = stopReasons.find(stopReasonKeyC);
- if (rit != stopReasons.end() && rit->second == breakPointStopReasonC) {
- const std::string breakpoints = gdbmiBreakpoints(exc.control(), exc.symbols(),
- false, false, &errorMessage);
- if (breakpoints.empty()) {
- str << ",breakpointserror=" << gdbmiStringFormat(errorMessage);
- } else {
- str << ",breakpoints=" << breakpoints;
- }
- }
str << '}';
reportLong('E', 0, "session_idle", str.str());
}
diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
index e3c45f178e..3e63d28a01 100644
--- a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
+++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp
@@ -679,11 +679,14 @@ static inline void formatGdbmiFlag(std::ostream &str, const char *name, bool v)
static bool gdbmiFormatBreakpoint(std::ostream &str,
IDebugBreakpoint *bp,
CIDebugSymbols *symbols /* = 0 */,
- bool verbose, std::string *errorMessage)
+ unsigned verbose, std::string *errorMessage)
{
enum { BufSize = 512 };
ULONG64 offset = 0;
ULONG flags = 0;
+ ULONG id = 0;
+ if (SUCCEEDED(bp->GetId(&id)))
+ str << ",id=\"" << id << '"';
HRESULT hr = bp->GetFlags(&flags);
if (FAILED(hr)) {
*errorMessage = msgDebugEngineComFailed("GetFlags", hr);
@@ -691,8 +694,8 @@ static bool gdbmiFormatBreakpoint(std::ostream &str,
}
const bool deferred = (flags & DEBUG_BREAKPOINT_DEFERRED) != 0;
formatGdbmiFlag(str, ",deferred", deferred);
+ formatGdbmiFlag(str, ",enabled", (flags & DEBUG_BREAKPOINT_ENABLED) != 0);
if (verbose) {
- formatGdbmiFlag(str, ",enabled", (flags & DEBUG_BREAKPOINT_ENABLED) != 0);
formatGdbmiFlag(str, ",oneshot", (flags & DEBUG_BREAKPOINT_ONE_SHOT) != 0);
str << ",flags=\"" << flags << '"';
ULONG threadId = 0;
@@ -713,16 +716,18 @@ static bool gdbmiFormatBreakpoint(std::ostream &str,
}
}
// Expression
- char buf[BufSize];
- if (SUCCEEDED(bp->GetOffsetExpression(buf, BUFSIZ, 0)))
- str << ",expression=\"" << gdbmiStringFormat(buf) << '"';
+ if (verbose > 1) {
+ char buf[BufSize];
+ if (SUCCEEDED(bp->GetOffsetExpression(buf, BUFSIZ, 0)))
+ str << ",expression=\"" << gdbmiStringFormat(buf) << '"';
+ }
return true;
}
// Format breakpoints as GDBMI
std::string gdbmiBreakpoints(CIDebugControl *ctrl,
CIDebugSymbols *symbols /* = 0 */,
- bool humanReadable, bool verbose, std::string *errorMessage)
+ bool humanReadable, unsigned verbose, std::string *errorMessage)
{
ULONG breakPointCount = 0;
HRESULT hr = ctrl->GetNumberBreakpoints(&breakPointCount);
@@ -735,7 +740,7 @@ std::string gdbmiBreakpoints(CIDebugControl *ctrl,
if (humanReadable)
str << '\n';
for (ULONG i = 0; i < breakPointCount; i++) {
- str << "{id=\"" << i << '"';
+ str << "{number=\"" << i << '"';
IDebugBreakpoint *bp = 0;
hr = ctrl->GetBreakpointByIndex(i, &bp);
if (FAILED(hr) || !bp) {
diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h
index 54c475bd9e..2a4facfbd0 100644
--- a/src/libs/qtcreatorcdbext/gdbmihelpers.h
+++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h
@@ -117,7 +117,7 @@ std::string gdbmiModules(CIDebugSymbols *syms, bool humanReadable, std::string *
std::string gdbmiBreakpoints(CIDebugControl *ctrl,
CIDebugSymbols *symbols /* = 0 */,
bool humanReadable,
- bool verbose,
+ unsigned verbose,
std::string *errorMessage);
/* Helpers for registers */
diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
index 59d6a3c2f2..602a004520 100644
--- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
+++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp
@@ -263,7 +263,7 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
int token;
commandTokens<StringList>(args, &token);
-
+ dprintf("Qt Creator CDB extension version 0.1 %d bit built %s.\n", sizeof(void *) > 4 ? 64 : 32, __DATE__);
if (const ULONG pid = currentProcessId(client)) {
ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid);
} else {
@@ -992,7 +992,7 @@ extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn)
int token;
std::string errorMessage;
bool humanReadable = false;
- bool verbose = false;
+ unsigned verbose = 0;
StringList tokens = commandTokens<StringList>(argsIn, &token);
while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') {
switch (tokens.front().at(1)) {
@@ -1000,7 +1000,7 @@ extern "C" HRESULT CALLBACK breakpoints(CIDebugClient *client, PCSTR argsIn)
humanReadable = true;
break;
case 'v':
- verbose = true;
+ verbose++;
break;
}
tokens.pop_front();
diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp
index 557f88386d..078f86a634 100644
--- a/src/plugins/debugger/breakhandler.cpp
+++ b/src/plugins/debugger/breakhandler.cpp
@@ -980,12 +980,22 @@ bool BreakHandler::needsChange(BreakpointId id) const
return it->needsChange();
}
-void BreakHandler::setResponse(BreakpointId id, const BreakpointResponse &data)
+void BreakHandler::setResponse(BreakpointId id, const BreakpointResponse &response, bool takeOver)
{
Iterator it = m_storage.find(id);
QTC_ASSERT(it != m_storage.end(), return);
- it->response = data;
- it->destroyMarker();
+ BreakpointItem &item = it.value();
+ item.response = response;
+ item.destroyMarker();
+ // Take over corrected values from response
+ if (takeOver) {
+ if (item.data.type == BreakpointByFileAndLine
+ && response.correctedLineNumber > 0)
+ item.data.lineNumber = response.correctedLineNumber;
+ if ((item.data.type == BreakpointByFileAndLine || item.data.type == BreakpointByFunction)
+ && !response.module.isEmpty())
+ item.data.module = response.module;
+ }
updateMarker(id);
}
@@ -1149,19 +1159,25 @@ QString BreakHandler::BreakpointItem::toToolTip() const
str << "<html><body><table>"
//<< "<tr><td>" << tr("Id:") << "</td><td>" << m_id << "</td></tr>"
<< "<tr><td>" << tr("State:")
- << "</td><td>" << state << " (" << stateToString(state) << ")</td></tr>"
- << "<tr><td>" << tr("Engine:")
- << "</td><td>" << (engine ? engine->objectName() : "0") << "</td></tr>"
- << "<tr><td>" << tr("Breakpoint Number:")
- << "</td><td>" << response.number << "</td></tr>"
- << "<tr><td>" << tr("Breakpoint Type:")
- << "</td><td>" << t << "</td></tr>"
- << "<tr><td>" << tr("Extra Information:")
- << "</td><td>" << response.extra << "</td></tr>"
- << "<tr><td>" << tr("Pending:")
- << "</td><td>" << (response.pending ? "True" : "False") << "</td></tr>"
- << "<tr><td>" << tr("Marker File:")
- << "</td><td>" << markerFileName() << "</td></tr>"
+ << "</td><td>" << (data.enabled ? tr("Enabled") : tr("Disabled"));
+ if (response.pending)
+ str << tr(", pending");
+ str << ", " << state << " (" << stateToString(state) << ")</td></tr>";
+ if (engine) {
+ str << "<tr><td>" << tr("Engine:")
+ << "</td><td>" << engine->objectName() << "</td></tr>";
+ }
+ if (!response.pending) {
+ str << "<tr><td>" << tr("Breakpoint Number:")
+ << "</td><td>" << response.number << "</td></tr>";
+ }
+ str << "<tr><td>" << tr("Breakpoint Type:")
+ << "</td><td>" << t << "</td></tr>";
+ if (!response.extra.isEmpty()) {
+ str << "<tr><td>" << tr("Extra Information:")
+ << "</td><td>" << response.extra << "</td></tr>"; }
+ str << "<tr><td>" << tr("Marker File:")
+ << "</td><td>" << QDir::toNativeSeparators(markerFileName()) << "</td></tr>"
<< "<tr><td>" << tr("Marker Line:")
<< "</td><td>" << markerLineNumber() << "</td></tr>"
<< "</table><br><hr><table>"
@@ -1199,28 +1215,32 @@ QString BreakHandler::BreakpointItem::toToolTip() const
formatAddress(str, data.address);
str << "</td><td>";
formatAddress(str, response.address);
- //str << "</td></tr>"
- // << "<tr><td>" << tr("Corrected Line Number:")
- // << "</td><td>-</td><td>";
- //if (response.bpCorrectedLineNumber > 0)
- // str << response.bpCorrectedLineNumber;
- //else
- // str << '-';
- str << "</td></tr>"
- << "<tr><td>" << tr("Condition:")
- << "</td><td>" << data.condition
- << "</td><td>" << response.condition << "</td></tr>"
- << "<tr><td>" << tr("Ignore Count:") << "</td><td>";
- if (data.ignoreCount)
- str << data.ignoreCount;
- str << "</td><td>";
- if (response.ignoreCount)
- str << response.ignoreCount;
- str << "</td></tr>"
- << "<tr><td>" << tr("Thread Specification:")
- << "</td><td>" << data.threadSpec
- << "</td><td>" << response.threadSpec << "</td></tr>"
- << "</table></body></html>";
+ if (!data.condition.isEmpty() || !response.condition.isEmpty()) {
+ str << "</td></tr>"
+ << "<tr><td>" << tr("Condition:")
+ << "</td><td>" << data.condition
+ << "</td><td>" << response.condition << "</td></tr>";
+ }
+ if (data.ignoreCount || response.ignoreCount) {
+ str << "<tr><td>" << tr("Ignore Count:") << "</td><td>";
+ if (data.ignoreCount)
+ str << data.ignoreCount;
+ str << "</td><td>";
+ if (response.ignoreCount)
+ str << response.ignoreCount;
+ }
+ if (data.threadSpec >= 0 || response.threadSpec >= 0) {
+ str << "</td></tr>"
+ << "<tr><td>" << tr("Thread Specification:")
+ << "</td><td>";
+ if (data.threadSpec >= 0)
+ str << data.threadSpec;
+ str << "</td><td>";
+ if (response.threadSpec >= 0)
+ str << response.threadSpec;
+ str << "</td></tr>";
+ }
+ str << "</table></body></html>";
return rc;
}
diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h
index a2338acdae..34dbd26e97 100644
--- a/src/plugins/debugger/breakhandler.h
+++ b/src/plugins/debugger/breakhandler.h
@@ -136,7 +136,7 @@ public:
DebuggerEngine *engine(BreakpointId id) const;
void setEngine(BreakpointId id, DebuggerEngine *engine);
const BreakpointResponse &response(BreakpointId id) const;
- void setResponse(BreakpointId id, const BreakpointResponse &data);
+ void setResponse(BreakpointId id, const BreakpointResponse &data, bool takeOver = true);
bool needsChange(BreakpointId id) const;
// State transitions.
diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp
index 9aae8f6878..41e41595c7 100644
--- a/src/plugins/debugger/breakwindow.cpp
+++ b/src/plugins/debugger/breakwindow.cpp
@@ -117,6 +117,12 @@ BreakpointDialog::BreakpointDialog(QWidget *parent)
connect(m_ui.comboBoxType, SIGNAL(activated(int)), SLOT(typeChanged(int)));
m_ui.lineEditIgnoreCount->setValidator(
new QIntValidator(0, 2147483647, m_ui.lineEditIgnoreCount));
+ const QString moduleToolTip =
+ tr("Specifying the module (base name of the library or executable)\n"
+ "for function or file type breakpoints can significantly speed up\n"
+ "debugger start-up times (CDB, LLDB).");
+ m_ui.labelModule->setToolTip(moduleToolTip);
+ m_ui.lineEditModule->setToolTip(moduleToolTip);
}
void BreakpointDialog::setType(BreakpointType type)
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index 3945be2b2d..435f282b2d 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -380,7 +380,6 @@ CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
m_accessible(false),
m_specialStopMode(NoSpecialStop),
m_nextCommandToken(0),
- m_nextBreakpointNumber(1),
m_currentBuiltinCommandIndex(-1),
m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
m_operateByInstructionPending(true),
@@ -1022,7 +1021,7 @@ void CdbEngine::executeRunToLine(const QString &fileName, int lineNumber)
BreakpointParameters bp(BreakpointByFileAndLine);
bp.fileName = fileName;
bp.lineNumber = lineNumber;
- postCommand(cdbAddBreakpointCommand(bp, true), 0);
+ postCommand(cdbAddBreakpointCommand(bp, BreakpointId(-1), true), 0);
continueInferior();
}
@@ -1032,7 +1031,7 @@ void CdbEngine::executeRunToFunction(const QString &functionName)
BreakpointParameters bp(BreakpointByFunction);
bp.functionName = functionName;
- postCommand(cdbAddBreakpointCommand(bp, true), 0);
+ postCommand(cdbAddBreakpointCommand(bp, BreakpointId(-1), true), 0);
continueInferior();
}
@@ -1607,8 +1606,15 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
}
const int threadId = stopReason.findChild("threadId").data().toInt();
if (reason == "breakpoint") {
- const int number = stopReason.findChild("breakpointId").data().toInt();
- const BreakpointId id = breakHandler()->findBreakpointByNumber(number);
+ // Note: Internal breakpoints (run to line) are reported with id=0.
+ BreakpointId id = 0;
+ int number = 0;
+ const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
+ if (breakpointIdG.isValid()) {
+ id = breakpointIdG.data().toULongLong();
+ if (id)
+ number = breakHandler()->response(id).number;
+ }
if (id && breakHandler()->type(id) == Watchpoint) {
*message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
} else {
@@ -1750,6 +1756,8 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
}
// Fire off remaining commands asynchronously
+ if (!m_pendingBreakpointMap.isEmpty())
+ postCommandSequence(CommandListBreakPoints);
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
postCommandSequence(CommandListRegisters);
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
@@ -2069,33 +2077,6 @@ static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bp
}
#endif
-// Figure out what kind of changes are required to synchronize
-enum BreakPointSyncType {
- BreakpointsUnchanged, BreakpointsAdded, BreakpointsRemovedChanged
-};
-
-static inline BreakPointSyncType breakPointSyncType(const BreakHandler *handler,
- const BreakpointIds ids)
-{
- bool added = false;
- foreach (BreakpointId id, ids) {
- const BreakpointState state = handler->state(id);
- if (debugBreakpoints > 1)
- qDebug(" Checking on breakpoint %llu, state %d\n", id, state);
- switch (state) {
- case BreakpointInsertRequested:
- added = true;
- break;
- case BreakpointChangeRequested:
- case BreakpointRemoveRequested:
- return BreakpointsRemovedChanged;
- default:
- break;
- }
- }
- return added ? BreakpointsAdded : BreakpointsUnchanged;
-}
-
bool CdbEngine::stateAcceptsBreakpointChanges() const
{
switch (state()) {
@@ -2124,13 +2105,33 @@ void CdbEngine::attemptBreakpointSynchronization()
if (acceptsBreakpoint(id))
handler->setEngine(id, this);
- // Find out if there is a need to synchronize again
+ // Quick check: is there a need to change something? - Populate module cache
+ bool changed = false;
const BreakpointIds ids = handler->engineBreakpointIds(this);
- const BreakPointSyncType syncType = breakPointSyncType(handler, ids);
+ foreach (BreakpointId id, ids) {
+ switch (handler->state(id)) {
+ case BreakpointInsertRequested:
+ case BreakpointRemoveRequested:
+ case BreakpointChangeRequested:
+ changed = true;
+ break;
+ case BreakpointInserted: {
+ // Collect the new modules matching the files.
+ // In the future, that information should be obtained from the build system.
+ const BreakpointParameters &data = handler->breakpointData(id);
+ if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
+ m_fileNameModuleHash.insert(data.fileName, data.module);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
if (debugBreakpoints)
- qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, syncType=%d",
- elapsedLogTime(), m_accessible, stateName(state()), ids.size(), syncType);
- if (syncType == BreakpointsUnchanged)
+ qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
+ elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
+ if (!changed)
return;
if (!m_accessible) {
@@ -2139,55 +2140,64 @@ void CdbEngine::attemptBreakpointSynchronization()
doInterruptInferior(SpecialStopSynchronizeBreakpoints);
return;
}
-
- // If there are changes/removals, delete all breakpoints and re-insert
- // all enabled breakpoints. This is the simplest
- // way to apply changes since CDB ids shift when removing breakpoints and there is no
- // easy way to re-match them.
-
- if (syncType == BreakpointsRemovedChanged) { // Need to clear out all?
- postCommand("bc *", 0);
- m_nextBreakpointNumber = 0;
- }
-
+ // Add/Change breakpoints and store pending ones in map, since
+ // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
+ // handleBreakPoints will the complete that information and set it on the break handler.
+ bool addedChanged = false;
foreach (BreakpointId id, ids) {
+ BreakpointParameters parameters = handler->breakpointData(id);
BreakpointResponse response;
- const BreakpointParameters &p = handler->breakpointData(id);
- response.fromParameters(p);
+ response.fromParameters(parameters);
+ // If we encountered that file and have a module for it: Add it.
+ if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
+ const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
+ if (it != m_fileNameModuleHash.constEnd())
+ parameters.module = it.value();
+ }
switch (handler->state(id)) {
case BreakpointInsertRequested:
- response.number = m_nextBreakpointNumber++;
- postCommand(cdbAddBreakpointCommand(p, false, response.number), 0);
+ postCommand(cdbAddBreakpointCommand(parameters, id, false), 0);
+ if (!parameters.enabled)
+ postCommand("bd " + QByteArray::number(id), 0);
handler->notifyBreakpointInsertProceeding(id);
handler->notifyBreakpointInsertOk(id);
- handler->setResponse(id, response);
+ m_pendingBreakpointMap.insert(id, response);
+ addedChanged = true;
+ if (debugBreakpoints)
+ qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
break;
case BreakpointChangeRequested:
- // Skip disabled breakpoints, else add.
handler->notifyBreakpointChangeProceeding(id);
- if (p.enabled) {
- response.number = m_nextBreakpointNumber++;
- postCommand(cdbAddBreakpointCommand(p, false, response.number), 0);
- handler->notifyBreakpointChangeOk(id);
+ if (parameters.enabled != handler->response(id).enabled) {
+ // Change enabled/disabled breakpoints without triggering update.
+ postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
+ response.pending = false;
+ response.enabled = parameters.enabled;
handler->setResponse(id, response);
+ } else {
+ // Delete and re-add, triggering update
+ addedChanged = true;
+ postCommand("bc " + QByteArray::number(id), 0);
+ postCommand(cdbAddBreakpointCommand(parameters, id, false), 0);
+ m_pendingBreakpointMap.insert(id, response);
}
+ handler->notifyBreakpointChangeOk(id);
+ if (debugBreakpoints)
+ qDebug("Changing %llu %s\n", id, qPrintable(response.toString()));
break;
case BreakpointRemoveRequested:
+ postCommand("bc " + QByteArray::number(id), 0);
handler->notifyBreakpointRemoveProceeding(id);
handler->notifyBreakpointRemoveOk(id);
- break;
- case BreakpointInserted:
- // Existing breakpoints were deleted due to change/removal, re-set
- if (syncType == BreakpointsRemovedChanged) {
- response.number = m_nextBreakpointNumber++;;
- postCommand(cdbAddBreakpointCommand(p, false, response.number), 0);
- handler->setResponse(id, response);
- }
+ m_pendingBreakpointMap.remove(id);
break;
default:
break;
}
}
+ // List breakpoints and send responses
+ if (addedChanged)
+ postCommandSequence(CommandListBreakPoints);
}
QString CdbEngine::normalizeFileName(const QString &f)
@@ -2308,6 +2318,11 @@ void CdbEngine::postCommandSequence(unsigned mask)
postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
return;
}
+ if (mask & CommandListBreakPoints) {
+ postExtensionCommand("breakpoints", QByteArray("-v"), 0,
+ &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
+ return;
+ }
}
void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
@@ -2342,6 +2357,81 @@ void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
m_watchPointX = m_watchPointY = 0;
}
+static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
+ QTextStream &str)
+{
+ str << "Obtained breakpoint " << id << " (#" << r.number << ')';
+ if (r.pending) {
+ str << ", pending";
+ } else {
+ str.setIntegerBase(16);
+ str << ", at 0x" << r.address;
+ str.setIntegerBase(10);
+ }
+ if (!r.enabled)
+ str << ", disabled";
+ if (!r.module.isEmpty())
+ str << ", module: '" << r.module << '\'';
+ str << '\n';
+}
+
+void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
+{
+ if (debugBreakpoints)
+ qDebug("CdbEngine::handleBreakPoints: sucess=%d: %s", reply->success, reply->reply.constData());
+ if (!reply->success) {
+ showMessage(QString::fromAscii(reply->errorMessage), LogError);
+ return;
+ }
+ GdbMi value;
+ value.fromString(reply->reply);
+ if (value.type() != GdbMi::List) {
+ showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
+ return;
+ }
+ handleBreakPoints(value);
+}
+
+void CdbEngine::handleBreakPoints(const GdbMi &value)
+{
+ // Report all obtained parameters back. Note that not all parameters are reported
+ // back, so, match by id and complete
+ if (debugBreakpoints)
+ qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
+ QString message;
+ QTextStream str(&message);
+ BreakHandler *handler = breakHandler();
+ foreach (const GdbMi &breakPointG, value.children()) {
+ BreakpointResponse reportedResponse;
+ const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
+ if (debugBreakpoints)
+ qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
+ qPrintable(reportedResponse.toString()));
+
+ if (!reportedResponse.pending) {
+ const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
+ if (it != m_pendingBreakpointMap.end()) {
+ // Complete the response and set on handler.
+ BreakpointResponse &currentResponse = it.value();
+ currentResponse.number = reportedResponse.number;
+ currentResponse.address = reportedResponse.address;
+ currentResponse.module = reportedResponse.module;
+ currentResponse.pending = reportedResponse.pending;
+ currentResponse.enabled = reportedResponse.enabled;
+ formatCdbBreakPointResponse(id, currentResponse, str);
+ handler->setResponse(id, currentResponse);
+ m_pendingBreakpointMap.erase(it);
+ }
+ } // not pending reported
+ } // foreach
+ if (m_pendingBreakpointMap.empty()) {
+ str << QLatin1String("All breakpoints have been resolved.\n");
+ } else {
+ str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
+ }
+ showMessage(message, LogMisc);
+}
+
void CdbEngine::watchPoint(const QPoint &p)
{
m_watchPointX = p.x();
diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h
index f34262382e..9a63b6a07a 100644
--- a/src/plugins/debugger/cdb/cdbengine.h
+++ b/src/plugins/debugger/cdb/cdbengine.h
@@ -35,6 +35,7 @@
#define DEBUGGER_CDBENGINE_H
#include "debuggerengine.h"
+#include "breakpoint.h"
#include <QtCore/QSharedPointer>
#include <QtCore/QProcess>
@@ -68,7 +69,8 @@ public:
CommandListStack = 0x1,
CommandListThreads = 0x2,
CommandListRegisters = 0x4,
- CommandListModules = 0x8
+ CommandListModules = 0x8,
+ CommandListBreakPoints = 0x10
};
typedef QSharedPointer<CdbBuiltinCommand> CdbBuiltinCommandPtr;
@@ -159,6 +161,8 @@ private slots:
void consoleStubExited();
private:
+ typedef QHash<BreakpointId, BreakpointResponse> PendingBreakPointMap;
+
enum SpecialStopMode
{
NoSpecialStop,
@@ -205,6 +209,8 @@ private:
void handleModules(const CdbExtensionCommandPtr &reply);
void handleMemory(const CdbExtensionCommandPtr &);
void handleWidgetAt(const CdbExtensionCommandPtr &);
+ void handleBreakPoints(const CdbExtensionCommandPtr &);
+ void handleBreakPoints(const GdbMi &value);
QString normalizeFileName(const QString &f);
void updateLocalVariable(const QByteArray &iname);
@@ -227,7 +233,6 @@ private:
bool m_accessible;
SpecialStopMode m_specialStopMode;
int m_nextCommandToken;
- int m_nextBreakpointNumber;
QList<CdbBuiltinCommandPtr> m_builtinCommandQueue;
int m_currentBuiltinCommandIndex; // Current command whose output is recorded.
QList<CdbExtensionCommandPtr> m_extensionCommandQueue;
@@ -244,6 +249,8 @@ private:
unsigned m_wX86BreakpointCount;
int m_watchPointX;
int m_watchPointY;
+ PendingBreakPointMap m_pendingBreakpointMap;
+ QHash<QString, QString> m_fileNameModuleHash;
};
} // namespace Internal
diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.cpp b/src/plugins/debugger/cdb/cdbparsehelpers.cpp
index 19ee19be87..32672fb437 100644
--- a/src/plugins/debugger/cdb/cdbparsehelpers.cpp
+++ b/src/plugins/debugger/cdb/cdbparsehelpers.cpp
@@ -54,7 +54,9 @@ namespace Debugger {
namespace Internal {
// Convert breakpoint in CDB syntax.
-QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn, bool oneshot, int id)
+QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn,
+ BreakpointId id /* = BreakpointId(-1) */,
+ bool oneshot)
{
#ifdef Q_OS_WIN
const BreakpointParameters bp = fixWinMSVCBreakpoint(bpIn);
@@ -68,8 +70,10 @@ QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn, bool onesho
if (bp.threadSpec >= 0)
str << '~' << bp.threadSpec << ' ';
- str << (bp.type == Watchpoint ? "ba" : "bp");
- if (id >= 0)
+ // Currently use 'bu' so that the offset expression (including file name)
+ // is kept when reporting back breakpoints (which is otherwise discarded when resolving).
+ str << (bp.type == Watchpoint ? "ba" : "bu");
+ if (id != BreakpointId(-1))
str << id;
str << ' ';
if (oneshot)
@@ -100,7 +104,7 @@ QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn, bool onesho
break;
}
if (bp.ignoreCount)
- str << ' ' << bp.ignoreCount;
+ str << ' ' << (bp.ignoreCount + 1);
// Condition currently unsupported.
return rc;
}
@@ -181,6 +185,66 @@ static inline bool parseThread(QByteArray line, ThreadData *thread, bool *curren
return true;
}
+// Helper to retrieve an int child from GDBMI
+static inline bool gdbmiChildToInt(const GdbMi &parent, const char *childName, int *target)
+{
+ const GdbMi childBA = parent.findChild(childName);
+ if (childBA.isValid()) {
+ bool ok;
+ const int v = childBA.data().toInt(&ok);
+ if (ok) {
+ *target = v;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Helper to retrieve an bool child from GDBMI
+static inline bool gdbmiChildToBool(const GdbMi &parent, const char *childName, bool *target)
+{
+ const GdbMi childBA = parent.findChild(childName);
+ if (childBA.isValid()) {
+ *target = childBA.data() == "true";
+ return true;
+ }
+ return false;
+}
+
+// Parse extension command listing breakpoints.
+// Note that not all fields are returned, since file, line, function are encoded
+// in the expression (that is in addition deleted on resolving for a bp-type breakpoint).
+BreakpointId parseBreakPoint(const GdbMi &gdbmi, BreakpointResponse *r,
+ QString *expression /* = 0 */)
+{
+ BreakpointId id = BreakpointId(-1);
+ gdbmiChildToInt(gdbmi, "number", &(r->number));
+ gdbmiChildToBool(gdbmi, "enabled", &(r->enabled));
+ gdbmiChildToBool(gdbmi, "deferred", &(r->pending));
+ const GdbMi idG = gdbmi.findChild("id");
+ if (idG.isValid()) { // Might not be valid if there is not id
+ bool ok;
+ const BreakpointId cid = idG.data().toULongLong(&ok);
+ if (ok)
+ id = cid;
+ }
+ const GdbMi moduleG = gdbmi.findChild("module");
+ if (moduleG.isValid())
+ r->module = QString::fromLocal8Bit(moduleG.data());
+ if (expression) {
+ const GdbMi expressionG = gdbmi.findChild("expression");
+ if (expressionG.isValid())
+ *expression = QString::fromLocal8Bit(expressionG.data());
+ }
+ const GdbMi addressG = gdbmi.findChild("address");
+ if (addressG.isValid())
+ r->address = addressG.data().toULongLong(0, 0);
+ if (gdbmiChildToInt(gdbmi, "passcount", &(r->ignoreCount)))
+ r->ignoreCount--;
+ gdbmiChildToInt(gdbmi, "thread", &(r->threadSpec));
+ return id;
+}
+
QString debugByteArray(const QByteArray &a)
{
QString rc;
diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.h b/src/plugins/debugger/cdb/cdbparsehelpers.h
index cc3d05f5c0..b65625b984 100644
--- a/src/plugins/debugger/cdb/cdbparsehelpers.h
+++ b/src/plugins/debugger/cdb/cdbparsehelpers.h
@@ -34,6 +34,8 @@
#ifndef CDBPARSEHELPERS_H
#define CDBPARSEHELPERS_H
+#include "breakpoint.h"
+
#include <QtCore/QtGlobal>
#include <QtCore/QList>
#include <QtCore/QVector>
@@ -53,7 +55,11 @@ class Register;
class GdbMi;
// Convert breakpoint in CDB syntax.
-QByteArray cdbAddBreakpointCommand(const BreakpointParameters &d, bool oneshot = false, int id = -1);
+QByteArray cdbAddBreakpointCommand(const BreakpointParameters &d, BreakpointId id = BreakpointId(-1), bool oneshot = false);
+// Parse extension command listing breakpoints.
+// Note that not all fields are returned, since file, line, function are encoded
+// in the expression (that is in addition deleted on resolving for a bp-type breakpoint).
+BreakpointId parseBreakPoint(const GdbMi &gdbmi, BreakpointResponse *r, QString *expression = 0);
// Convert a CDB integer value: '00000000`0012a290' -> '12a290', '0n10' ->'10'
QByteArray fixCdbIntegerValue(QByteArray t, bool stripLeadingZeros = false, int *basePtr = 0);