From 435b4608d92c2be3868e470e184d3d0fde3935c1 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 23 Mar 2011 15:08:57 +0100 Subject: [PATCH] Debugger[CDB]: Add a pretense to breakpoint conditions. Add the infrastructure for checking on breakpoint conditions on the QtCreator side (for conditions, bitfield watchpoints). Have cdb evaluate breakpoint conditions as integer expressions. --- src/libs/qtcreatorcdbext/common.h | 2 +- src/libs/qtcreatorcdbext/gdbmihelpers.cpp | 51 ++++++++++ src/libs/qtcreatorcdbext/gdbmihelpers.h | 6 ++ src/libs/qtcreatorcdbext/qtcreatorcdbext.def | 1 + .../qtcreatorcdbext/qtcreatorcdbextension.cpp | 28 ++++++ src/plugins/debugger/cdb/cdbengine.cpp | 92 +++++++++++++++++-- src/plugins/debugger/cdb/cdbengine.h | 6 +- 7 files changed, 178 insertions(+), 8 deletions(-) diff --git a/src/libs/qtcreatorcdbext/common.h b/src/libs/qtcreatorcdbext/common.h index 4d4075ab5b9..0f1caf4cbf5 100644 --- a/src/libs/qtcreatorcdbext/common.h +++ b/src/libs/qtcreatorcdbext/common.h @@ -46,7 +46,7 @@ #include #include -typedef IDebugControl CIDebugControl; +typedef IDebugControl3 CIDebugControl; typedef IDebugSymbols3 CIDebugSymbols; typedef IDebugSymbolGroup2 CIDebugSymbolGroup; typedef IDebugClient5 CIDebugClient; diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp index 3e63d28a017..fc3b747e5d0 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp @@ -756,3 +756,54 @@ std::string gdbmiBreakpoints(CIDebugControl *ctrl, str << ']'; return str.str(); } + +std::string msgEvaluateExpressionFailed(const std::string &expression, + const std::string &why) +{ + std::ostringstream str; + str << "Failed to evaluate expression '" << expression << "': " << why; + return str.str(); +} + +bool evaluateExpression(CIDebugControl *control, const std::string expression, + ULONG desiredType, DEBUG_VALUE *v, std::string *errorMessage) +{ + // Ensure we are in C++ + ULONG oldSyntax; + HRESULT hr = control->GetExpressionSyntax(&oldSyntax); + if (FAILED(hr)) { + *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("GetExpressionSyntax", hr)); + return false; + } + if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) { + HRESULT hr = control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS); + if (FAILED(hr)) { + *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr)); + return false; + } + } + hr = control->Evaluate(expression.c_str(), desiredType, v, NULL); + if (FAILED(hr)) { + *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("Evaluate", hr)); + return false; + } + if (oldSyntax != DEBUG_EXPR_CPLUSPLUS) { + HRESULT hr = control->SetExpressionSyntax(oldSyntax); + if (FAILED(hr)) { + *errorMessage = msgEvaluateExpressionFailed(expression, msgDebugEngineComFailed("SetExpressionSyntax", hr)); + return false; + } + } + return true; +} + +bool evaluateInt64Expression(CIDebugControl *control, const std::string expression, + LONG64 *v, std::string *errorMessage) +{ + *v= 0; + DEBUG_VALUE dv; + if (!evaluateExpression(control, expression, DEBUG_VALUE_INT64, &dv, errorMessage)) + return false; + *v = dv.I64; + return true; +} diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h index 2a4facfbd0e..4b77cc74919 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.h +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h @@ -173,4 +173,10 @@ std::string gdbmiStack(CIDebugControl *debugControl, CIDebugSymbols *debugSymbol std::string widgetAt(const SymbolGroupValueContext &ctx, int x, int y, std::string *errorMessage); +bool evaluateExpression(CIDebugControl *control, const std::string expression, + ULONG desiredType, DEBUG_VALUE *v, std::string *errorMessage); + +bool evaluateInt64Expression(CIDebugControl *control, const std::string expression, + LONG64 *, std::string *errorMessage); + #endif // THREADLIST_H diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def index ab4cf4ea5c8..798be24b9d7 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def @@ -16,6 +16,7 @@ modules idle help memory +expression shutdownex test stack diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index d0e354dd1a5..16b27585776 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -107,6 +107,7 @@ enum Command { CmdIdle, CmdHelp, CmdMemory, + CmdExpression, CmdStack, CmdShutdownex, CmdAddWatch, @@ -165,6 +166,7 @@ static const CommandDescription commandDescriptions[] = { "Intended to be used with .idle_cmd to obtain proper stop notification.",""}, {"help","Prints help.",""}, {"memory","Prints memory contents in Base64 encoding.","[-t token]
"}, +{"expression","Prints expression value.","[-t token] "}, {"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"}, {"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""}, {"addwatch","Add watch expression"," "}, @@ -924,6 +926,32 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn) return S_OK; } +extern "C" HRESULT CALLBACK expression(CIDebugClient *Client, PCSTR argsIn) +{ + ExtensionCommandContext exc(Client); + std::string errorMessage; + LONG64 value = 0; + int token = 0; + + do { + const StringVector tokens = commandTokens(argsIn, &token); + if (tokens.size() != 1) { + + errorMessage = singleLineUsage(commandDescriptions[CmdExpression]); + break; + } + if (!evaluateInt64Expression(exc.control(), tokens.front(), &value, &errorMessage)) + break; + } while (false); + + if (errorMessage.empty()) { + ExtensionContext::instance().reportLong('R', token, "expression", toString(value)); + } else { + ExtensionContext::instance().report('N', token, 0, "expression", errorMessage.c_str()); + } + return S_OK; +} + // Extension command 'stack' // Report stack correctly as 'k' does not list instruction pointer // correctly. diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 949bed5fb45..94611957ae8 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -183,11 +183,19 @@ struct MemoryChangeCookie QByteArray data; }; +struct ConditionalBreakPointCookie +{ + ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {} + BreakpointId id; + GdbMi stopReason; +}; + } // namespace Internal } // namespace Debugger Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie) Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie) +Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie) namespace Debugger { namespace Internal { @@ -1051,6 +1059,7 @@ unsigned CdbEngine::debuggerCapabilities() const return DisassemblerCapability | RegisterCapability | ShowMemoryCapability |WatchpointCapability|JumpToLineCapability|AddWatcherCapability |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw(). + |BreakConditionCapability|TracePointCapability |BreakModuleCapability; } @@ -1755,9 +1764,25 @@ enum StopActionFlags StopShutdownInProgress = 0x80 // Shutdown in progress }; +static inline QString msgTracePointTriggered(BreakpointId id, const int number, + const QString &threadId) +{ + return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.") + .arg(id).arg(number).arg(threadId); +} + +static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number, + const QByteArray &condition, + const QString &threadId) +{ + return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.") + .arg(id).arg(number).arg(threadId, QString::fromAscii(condition)); +} + unsigned CdbEngine::examineStopReason(const GdbMi &stopReason, QString *message, - QString *exceptionBoxMessage) + QString *exceptionBoxMessage, + bool conditionalBreakPointTriggered) { // Report stop reason (GDBMI) unsigned rc = 0; @@ -1788,7 +1813,22 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason, if (breakpointIdG.isValid()) { id = breakpointIdG.data().toULongLong(); if (id && breakHandler()->engineBreakpointIds(this).contains(id)) { - number = breakHandler()->response(id).number; + const BreakpointResponse parameters = breakHandler()->response(id); + // Trace point? Just report. + number = parameters.number; + if (parameters.tracepoint) { + *message = msgTracePointTriggered(id, number, QString::number(threadId)); + return StopReportLog|StopIgnoreContinue; + } + // Trigger evaluation of BP expression unless we are already in the response. + if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) { + *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition, + QString::number(threadId)); + ConditionalBreakPointCookie cookie(id); + cookie.stopReason = stopReason; + evaluateExpression(parameters.condition, qVariantFromValue(cookie)); + return StopReportLog; + } } else { id = 0; } @@ -1879,14 +1919,19 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA) notifyEngineSetupOk(); return; } + GdbMi stopReason; + stopReason.fromString(messageBA); + processStop(stopReason, false); +} +void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered) +{ // Further examine stop and report to user QString message; QString exceptionBoxMessage; - GdbMi stopReason; - stopReason.fromString(messageBA); int forcedThreadId = -1; - const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage); + const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage, + conditionalBreakPointTriggered); // Do the non-blocking log reporting if (stopFlags & StopReportLog) showMessage(message, LogMisc); @@ -1894,7 +1939,7 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA) showStatusMessage(message); if (stopFlags & StopReportParseError) showMessage(message, LogError); - // Ignore things like WOW64 + // Ignore things like WOW64, report tracepoints. if (stopFlags & StopIgnoreContinue) { postCommand("g", 0); return; @@ -2513,6 +2558,41 @@ void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command) } } +void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command) +{ + int value = 0; + if (command->success) { + value = command->reply.toInt(); + } else { + showMessage(command->errorMessage, LogError); + } + // Is this a conditional breakpoint? + if (command->cookie.isValid() && qVariantCanConvert(command->cookie)) { + const ConditionalBreakPointCookie cookie = qvariant_cast(command->cookie); + const QString message = value ? + tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping."). + arg(value).arg(cookie.id) : + tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing."). + arg(cookie.id); + showMessage(message, LogMisc); + // Stop if evaluation is true, else continue + if (value) { + processStop(cookie.stopReason, true); + } else { + postCommand("g", 0); + } + } +} + +void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie) +{ + if (exp.contains(' ') && !exp.startsWith('"')) { + exp.prepend('"'); + exp.append('"'); + } + postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie); +} + void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command) { postCommandSequence(command->commandSequence); diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 4e024d0e52c..d7e1ea98e22 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -191,7 +191,9 @@ private: bool startConsole(const DebuggerStartParameters &sp, QString *errorMessage); void init(); unsigned examineStopReason(const GdbMi &stopReason, QString *message, - QString *exceptionBoxMessage); + QString *exceptionBoxMessage, + bool conditionalBreakPointTriggered = false); + void processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered = false); bool commandsPending() const; void handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message); bool doSetupEngine(QString *errorMessage); @@ -208,6 +210,7 @@ private: void syncOperateByInstruction(bool operateByInstruction); void postWidgetAtCommand(); void handleCustomSpecialStop(const QVariant &v); + void evaluateExpression(QByteArray exp, const QVariant &cookie = QVariant()); // Builtin commands void dummyHandler(const CdbBuiltinCommandPtr &); @@ -215,6 +218,7 @@ private: void handleRegisters(const CdbBuiltinCommandPtr &); void handleDisassembler(const CdbBuiltinCommandPtr &); void handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &); + void handleExpression(const CdbExtensionCommandPtr &); void jumpToAddress(quint64 address); // Extension commands void handleThreads(const CdbExtensionCommandPtr &);