diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp index 0b97395afe2cee56f1271014d2e7699dfe5bcf83..58d2d7824647d93c136663a896f0aafb46bdda8f 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.cpp +++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp @@ -48,7 +48,7 @@ const char *ExtensionContext::stopReasonKeyC = "reason"; ExtensionContext::ExtensionContext() : m_hookedClient(0), m_oldEventCallback(0), m_oldOutputCallback(0), - m_creatorEventCallback(0), m_creatorOutputCallback(0) + m_creatorEventCallback(0), m_creatorOutputCallback(0), m_stateNotification(true) { } @@ -81,6 +81,20 @@ void ExtensionContext::hookCallbacks(CIDebugClient *client) } } +void ExtensionContext::startRecordingOutput() +{ + if (m_creatorOutputCallback) { + m_creatorOutputCallback->startRecording(); + } else { + report('X', 0, 0, "Error", "ExtensionContext::startRecordingOutput() called with no output hooked.\n"); + } +} + +std::wstring ExtensionContext::stopRecordingOutput() +{ + return m_creatorOutputCallback ? m_creatorOutputCallback->stopRecording() : std::wstring(); +} + void ExtensionContext::setStopReason(const StopReasonMap &r, const std::string &reason) { m_stopReason = r; @@ -163,37 +177,41 @@ static inline ExtensionContext::StopReasonMap void ExtensionContext::notifyIdle() { discardSymbolGroup(); - - const StopReasonMap stopReasons = completeStopReasons(m_stopReason, executionStatus()); - m_stopReason.clear(); - // Format - std::ostringstream str; - formatGdbmiHash(str, stopReasons); - reportLong('E', 0, "session_idle", str.str()); + if (m_stateNotification) { + const StopReasonMap stopReasons = completeStopReasons(m_stopReason, executionStatus()); + // Format + std::ostringstream str; + formatGdbmiHash(str, stopReasons); + reportLong('E', 0, "session_idle", str.str()); + } m_stopReason.clear(); } void ExtensionContext::notifyState(ULONG Notify) { const ULONG ex = executionStatus(); - switch (Notify) { - case DEBUG_NOTIFY_SESSION_ACTIVE: - report('E', 0, 0, "session_active", "%u", ex); - break; - case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted - report('E', 0, 0, "session_accessible", "%u", ex); - break; - case DEBUG_NOTIFY_SESSION_INACCESSIBLE: - report('E', 0, 0, "session_inaccessible", "%u", ex); - break; - case DEBUG_NOTIFY_SESSION_INACTIVE: - report('E', 0, 0, "session_inactive", "%u", ex); + if (m_stateNotification) { + switch (Notify) { + case DEBUG_NOTIFY_SESSION_ACTIVE: + report('E', 0, 0, "session_active", "%u", ex); + break; + case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted + report('E', 0, 0, "session_accessible", "%u", ex); + break; + case DEBUG_NOTIFY_SESSION_INACCESSIBLE: + report('E', 0, 0, "session_inaccessible", "%u", ex); + break; + case DEBUG_NOTIFY_SESSION_INACTIVE: + report('E', 0, 0, "session_inactive", "%u", ex); + break; + } + } + if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE) { discardSymbolGroup(); discardWatchesSymbolGroup(); // We lost the debuggee, at this point restore output. if (ex & DEBUG_STATUS_NO_DEBUGGEE) unhookCallbacks(); - break; } } @@ -280,6 +298,48 @@ bool ExtensionContext::reportLong(char code, int token, const char *serviceName, return true; } +bool ExtensionContext::call(const std::string &functionCall, + std::wstring *output, + std::string *errorMessage) +{ + if (!m_creatorOutputCallback) { + *errorMessage = "Attempt to issue a call with no output hooked."; + return false; + } + // Set up arguments + const std::string call = ".call " + functionCall; + HRESULT hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, call.c_str(), DEBUG_EXECUTE_ECHO); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("Execute", hr); + return 0; + } + // Execute in current thread. TODO: This must not crash, else we are in an inconsistent state + // (need to call 'gh', etc.) + hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "~. g", DEBUG_EXECUTE_ECHO); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("Execute", hr); + return 0; + } + // Wait until finished + startRecordingOutput(); + m_stateNotification = false; + m_control->WaitForEvent(0, INFINITE); + *output = stopRecordingOutput(); + m_stateNotification = true; + // Crude attempt at recovering from a crash: Issue 'gN' (go with exception not handled). + const bool crashed = output->find(L"This exception may be expected and handled.") != std::string::npos; + if (crashed) { + m_stopReason.clear(); + m_stateNotification = false; + hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "~. gN", DEBUG_EXECUTE_ECHO); + m_control->WaitForEvent(0, INFINITE); + m_stateNotification = true; + *errorMessage = "A crash occurred while calling: " + functionCall; + return false; + } + return true; +} + // Exported C-functions extern "C" { diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h index 8ff9807cb7389654a0f97e123fc18c0c6d22ceb8..27a3f76b22e1e33a945098135ed184e5c0951125 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.h +++ b/src/libs/qtcreatorcdbext/extensioncontext.h @@ -42,6 +42,7 @@ class LocalsSymbolGroup; class WatchesSymbolGroup; +class OutputCallback; // Global singleton with context. // Caches a symbolgroup per frame and thread as long as the session is accessible. @@ -97,6 +98,11 @@ public: // Set a stop reason to be reported with the next idle notification (exception). void setStopReason(const StopReasonMap &, const std::string &reason = std::string()); + void startRecordingOutput(); + std::wstring stopRecordingOutput(); + // Execute a function call and record the output. + bool call(const std::string &functionCall, std::wstring *output, std::string *errorMessage); + private: bool isInitialized() const; void discardSymbolGroup(); @@ -109,9 +115,10 @@ private: IDebugEventCallbacks *m_oldEventCallback; IDebugOutputCallbacksWide *m_oldOutputCallback; IDebugEventCallbacks *m_creatorEventCallback; - IDebugOutputCallbacksWide *m_creatorOutputCallback; + OutputCallback *m_creatorOutputCallback; StopReasonMap m_stopReason; + bool m_stateNotification; }; // Context for extension commands to be instantiated on stack in a command handler. diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp index d36fb6f7cad3043bd6a9e8ced6efa10cebf530cd..6826df9b83ad074f29e43093a07cd38a0f7c3b09 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.cpp +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.cpp @@ -35,6 +35,8 @@ #include "stringutils.h" #include "iinterfacepointer.h" #include "base64.h" +#include "symbolgroupvalue.h" +#include "extensioncontext.h" #include <vector> @@ -620,3 +622,51 @@ std::string gdbmiStack(CIDebugControl *debugControl, str << ']'; return str.str(); } + +// Find the widget of the application by calling QApplication::widgetAt(). +// Return "Qualified_ClassName:Address" + +static inline std::string msgWidgetParseError(std::wstring wo) +{ + replace(wo, L'\n', L';'); + return "Output parse error :" + wStringToString(wo); +} + +std::string widgetAt(const SymbolGroupValueContext &ctx, int x, int y, std::string *errorMessage) +{ + typedef SymbolGroupValue::SymbolList SymbolList; + // First, resolve symbol since there are ambiguities. Take the first one which is the + // overload for (int,int) and call by address instead off name to overcome that. + const std::string func = QtInfo::get(ctx).prependQtGuiModule("QApplication::widgetAt"); + const SymbolList symbols = SymbolGroupValue::resolveSymbol(func.c_str(), ctx, errorMessage); + if (symbols.empty()) + return std::string(); // Not a gui application, likely + std::ostringstream callStr; + callStr << std::showbase << std::hex << symbols.front().second + << std::noshowbase << std::dec << '(' << x << ',' << y << ')'; + std::wstring wOutput; + if (!ExtensionContext::instance().call(callStr.str(), &wOutput, errorMessage)) + return std::string(); + // Returns: ".call returns\nclass QWidget * 0x00000000`022bf100\nbla...". + // Chop lines in front and after 'class ...' and convert first line. + const std::wstring::size_type classPos = wOutput.find(L"class "); + if (classPos == std::wstring::npos) { + *errorMessage = msgWidgetParseError(wOutput); + return std::string(); + } + wOutput.erase(0, classPos + 6); + const std::wstring::size_type nlPos = wOutput.find(L'\n'); + if (nlPos != std::wstring::npos) + wOutput.erase(nlPos, wOutput.size() - nlPos); + const std::string::size_type addressPos = wOutput.find(L" * 0x"); + if (addressPos == std::string::npos) { + *errorMessage = msgWidgetParseError(wOutput); + return std::string(); + } + // "QWidget * 0x00000000`022bf100" -> "QWidget:0x00000000022bf100" + wOutput.replace(addressPos, 3, L":"); + const std::string::size_type sepPos = wOutput.find(L'`'); + if (sepPos != std::string::npos) + wOutput.erase(sepPos, 1); + return wStringToString(wOutput); +} diff --git a/src/libs/qtcreatorcdbext/gdbmihelpers.h b/src/libs/qtcreatorcdbext/gdbmihelpers.h index fee71a423137c2928f03893f4adedb0b7e1d49a9..849a05faebbfb9a44d0b6e90ee9e6b633212a96e 100644 --- a/src/libs/qtcreatorcdbext/gdbmihelpers.h +++ b/src/libs/qtcreatorcdbext/gdbmihelpers.h @@ -37,6 +37,8 @@ #include "common.h" #include <vector> +struct SymbolGroupValueContext; + /* Various helpers to the extension commands to retrieve debuggee information * in suitable formats for the debugger engine. */ @@ -159,4 +161,9 @@ std::string gdbmiStack(CIDebugControl *debugControl, CIDebugSymbols *debugSymbol unsigned maxFrames, bool humanReadable, std::string *errorMessage); +// Find the widget of the application at (x,y) by calling QApplication::widgetAt(). +// Return a string of "Qualified_ClassName:Address" +std::string widgetAt(const SymbolGroupValueContext &ctx, + int x, int y, std::string *errorMessage); + #endif // THREADLIST_H diff --git a/src/libs/qtcreatorcdbext/outputcallback.cpp b/src/libs/qtcreatorcdbext/outputcallback.cpp index 63dde47e5ea49e110d23f12970ca67d9828fb436..cb3c57ed114d557436b3112ecc683bf92f31de1c 100644 --- a/src/libs/qtcreatorcdbext/outputcallback.cpp +++ b/src/libs/qtcreatorcdbext/outputcallback.cpp @@ -38,7 +38,8 @@ #include <cstring> -OutputCallback::OutputCallback(IDebugOutputCallbacksWide *wrapped) : m_wrapped(wrapped) +OutputCallback::OutputCallback(IDebugOutputCallbacksWide *wrapped) : + m_wrapped(wrapped), m_recording(false) { } @@ -85,6 +86,9 @@ STDMETHODIMP OutputCallback::Output( IN PCWSTR text ) { + + if (m_recording) + m_recorded.append(text); // Do not unconditionally output ourselves here, as this causes an endless // recursion. Suppress prompts (note that sequences of prompts may mess parsing up) if (!m_wrapped || mask == DEBUG_OUTPUT_PROMPT) @@ -100,3 +104,17 @@ STDMETHODIMP OutputCallback::Output( ExtensionContext::instance().reportLong('E', 0, "debuggee_output", str.str().c_str()); return S_OK; } + +void OutputCallback::startRecording() +{ + m_recorded.clear(); + m_recording = true; +} + +std::wstring OutputCallback::stopRecording() +{ + const std::wstring rc = m_recorded; + m_recorded.clear(); + m_recording = false; + return rc; +} diff --git a/src/libs/qtcreatorcdbext/outputcallback.h b/src/libs/qtcreatorcdbext/outputcallback.h index 4ba81a218b8ef9cd02bb206e693329855ce914b0..4432ab3821b19cea81cd96470d56f12daccae108 100644 --- a/src/libs/qtcreatorcdbext/outputcallback.h +++ b/src/libs/qtcreatorcdbext/outputcallback.h @@ -63,8 +63,13 @@ public: IN PCWSTR text ); + void startRecording(); + std::wstring stopRecording(); + private: IDebugOutputCallbacksWide *m_wrapped; + bool m_recording; + std::wstring m_recorded; }; #endif // DEBUGEVENTOUTPUT_H diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def index e8c47d447e82ebb5d2e2d5e3d7ae00ba302c4cb7..bc91011c9b5776c0a7584840023e0df237b6b35c 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def @@ -20,4 +20,5 @@ shutdownex test stack addwatch +widgetat KnownStructOutput diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 706bb055ac03d2a77556268f33f066784b261f3e..ef3fd00ec0abd73c7e9d798dddc2d7b8be5dc2dd 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -100,6 +100,7 @@ enum Command { CmdStack, CmdShutdownex, CmdAddWatch, + CmdWidgetAt, CmdTest }; @@ -155,6 +156,7 @@ static const CommandDescription commandDescriptions[] = { {"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","<iname> <expression>"}, +{"widgetat","Return address of widget at position","<x> <y>"}, {"test","Testing command","-T type | -w watch-expression"} }; @@ -943,6 +945,38 @@ extern "C" HRESULT CALLBACK shutdownex(CIDebugClient *, PCSTR) return S_OK; } +extern "C" HRESULT CALLBACK widgetat(CIDebugClient *client, PCSTR argsIn) +{ + ExtensionCommandContext exc(client); + int token = 0; + std::string widgetAddress; + std::string errorMessage; + + do { + int x = -1; + int y = -1; + + const StringVector tokens = commandTokens<StringVector>(argsIn, &token); + if (tokens.size() != 2) { + errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]); + break; + } + if (!integerFromString(tokens.front(), &x) || !integerFromString(tokens.at(1), &y)) { + errorMessage = singleLineUsage(commandDescriptions[CmdWidgetAt]); + break; + } + widgetAddress = widgetAt(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), + x, y, &errorMessage); + } while (false); + + if (widgetAddress.empty()) { + ExtensionContext::instance().report('N', token, 0, "widgetat", errorMessage.c_str()); + } else { + ExtensionContext::instance().reportLong('R', token, "widgetat", widgetAddress); + } + return S_OK; +} + extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn) { enum Mode { Invalid, TestType, TestFixWatchExpression }; diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index 1b5de07e55007879a61a79afcf2f178c538f3746..6aaa6185a83889c7dc4c8eedd1765e20d619c121 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -701,7 +701,6 @@ static bool parseWatchExpression(const std::string &expression, for ( ; pos < size ; pos++) { const char c = expression.at(pos); const WatchExpressionParseState nextState = nextWatchExpressionParseState(state, c, &templateLevel); - DebugPrint() << c << ' ' << pos << ' ' << state << ' ' << nextState << ' ' << templateLevel; if (nextState == WEPS_Error) return false; if (nextState != state && state == WEPS_WithinType) @@ -767,16 +766,19 @@ bool WatchesSymbolGroup::addWatch(CIDebugSymbols *s, std::string iname, const st return true; } -// Compile map of current state root-iname->root-expression +// Compile map of current state root-iname->root-expression (top-level) WatchesSymbolGroup::InameExpressionMap WatchesSymbolGroup::currentInameExpressionMap() const { + // Skip additional, expanded nodes InameExpressionMap rc; - if (unsigned size = unsigned(root()->children().size())) + if (unsigned size = unsigned(root()->children().size())) { for (unsigned i = 0; i < size; i++) { const AbstractSymbolGroupNode *n = root()->childAt(i); - rc.insert(InameExpressionMap::value_type(n->iName(), n->name())); + if (n->testFlags(SymbolGroupNode::WatchNode)) + rc.insert(InameExpressionMap::value_type(n->iName(), n->name())); } + } return rc; } diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index ec5fd2b54e192cdfe55cb96186f3ccd445844008..1bcd596ff3dbf0ccd1093fb87f766855c3158d07 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -398,7 +398,7 @@ static inline std::string resolveQtSymbol(const char *symbolC, std::string defaultPattern = defaultModuleNameC; defaultPattern.push_back('!'); defaultPattern += symbolC; - const StringList defaultMatches = SymbolGroupValue::resolveSymbol(defaultPattern.c_str(), ctx); + const StringList defaultMatches = SymbolGroupValue::resolveSymbolName(defaultPattern.c_str(), ctx); const SubStringPredicate modulePattern(modulePatternC); const StringListConstIt defaultIt = std::find_if(defaultMatches.begin(), defaultMatches.end(), modulePattern); if (defaultIt != defaultMatches.end()) @@ -406,7 +406,7 @@ static inline std::string resolveQtSymbol(const char *symbolC, // Fail, now try a search with '*qstrdup' in all modules. This might return several matches // like 'QtCored4!qstrdup', 'QGuid4!qstrdup' const std::string wildCardPattern = std::string(1, '*') + symbolC; - const StringList allMatches = SymbolGroupValue::resolveSymbol(wildCardPattern.c_str(), ctx); + const StringList allMatches = SymbolGroupValue::resolveSymbolName(wildCardPattern.c_str(), ctx); const StringListConstIt allIt = std::find_if(allMatches.begin(), allMatches.end(), modulePattern); return allIt != allMatches.end() ? *allIt : std::string(); } @@ -491,12 +491,29 @@ std::ostream &operator<<(std::ostream &os, const QtInfo &i) } std::list<std::string> + SymbolGroupValue::resolveSymbolName(const char *pattern, + const SymbolGroupValueContext &c, + std::string *errorMessage /* = 0 */) +{ + // Extract the names + const SymbolList symbols = resolveSymbol(pattern, c, errorMessage); + std::list<std::string> rc; + if (!symbols.empty()) { + const SymbolList::const_iterator cend = symbols.end(); + for (SymbolList::const_iterator it = symbols.begin(); it != cend; ++it) + rc.push_back(it->first); + } + return rc; + +} + +SymbolGroupValue::SymbolList SymbolGroupValue::resolveSymbol(const char *pattern, const SymbolGroupValueContext &c, std::string *errorMessage /* = 0 */) { enum { bufSize = 2048 }; - std::list<std::string> rc; + std::list<Symbol> rc; if (errorMessage) errorMessage->clear(); // Is it an incomplete symbol? @@ -518,12 +535,13 @@ std::list<std::string> return rc; } char buf[bufSize]; + ULONG64 offset; while (true) { - hr = c.symbols->GetNextSymbolMatch(handle, buf, bufSize - 1, 0, 0); + hr = c.symbols->GetNextSymbolMatch(handle, buf, bufSize - 1, 0, &offset); if (hr == E_NOINTERFACE) break; if (hr == S_OK) - rc.push_back(std::string(buf)); + rc.push_back(Symbol(std::string(buf), offset)); } c.symbols->EndSymbolMatch(handle); return rc; diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h index 689a5123404c2bcd73d62b2d88cdc8157bbb5541..3375f3e6b78b631c6a9179b1c26d6175c954e80f 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.h +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -70,6 +70,9 @@ class SymbolGroupValue explicit SymbolGroupValue(const std::string &parentError); public: + typedef std::pair<std::string, ULONG64> Symbol; + typedef std::list<Symbol> SymbolList; + explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c); SymbolGroupValue(); @@ -129,9 +132,13 @@ public: const SymbolGroupValueContext &ctx, const std::string ¤tModule = std::string()); - static std::list<std::string> resolveSymbol(const char *pattern, - const SymbolGroupValueContext &c, - std::string *errorMessage = 0); + static std::list<std::string> resolveSymbolName(const char *pattern, + const SymbolGroupValueContext &c, + std::string *errorMessage = 0); + static SymbolList resolveSymbol(const char *pattern, + const SymbolGroupValueContext &c, + std::string *errorMessage = 0); + static unsigned pointerSize(); static unsigned intSize(); diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 78ec65d5b0477eb2cc0b42fecefa9a537278c5c9..d6d8440035b868bec1b51e97e4f08a29de62808b 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -353,7 +353,9 @@ CdbEngine::CdbEngine(const DebuggerStartParameters &sp, m_hasDebuggee(false), m_elapsedLogTime(0), m_sourceStepInto(false), - m_wX86BreakpointCount(0) + m_wX86BreakpointCount(0), + m_watchPointX(0), + m_watchPointY(0) { Utils::SavedAction *assemblerAction = theAssemblerAction(); m_operateByInstructionPending = assemblerAction->isChecked(); @@ -1443,6 +1445,12 @@ unsigned CdbEngine::examineStopReason(const QByteArray &messageIn, *message = tr("Malformed stop response received."); return StopReportParseError|StopNotifyStop; } + // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log. + if (state() == InferiorStopOk) { + *message = QString::fromLatin1("Ignored stop notification from function call (%1)."). + arg(QString::fromAscii(reason)); + return StopReportLog; + } const int threadId = stopReason.findChild("threadId").data().toInt(); if (reason == "breakpoint") { const int number = stopReason.findChild("breakpointId").data().toInt(); @@ -1511,6 +1519,9 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA) attemptBreakpointSynchronization(); doContinueInferior(); return; + case SpecialStopGetWidgetAt: + postWidgetAtCommand(); + return; case NoSpecialStop: break; } @@ -2112,5 +2123,67 @@ void CdbEngine::postCommandSequence(unsigned mask) } } +void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply) +{ + bool success = false; + QString message; + do { + if (!reply->success) { + message = QString::fromAscii(reply->errorMessage); + break; + } + // Should be "namespace::QWidget:0x555" + QString watchExp = QString::fromAscii(reply->reply); + const int sepPos = watchExp.lastIndexOf(QLatin1Char(':')); + if (sepPos == -1) { + message = QString::fromAscii("Invalid output: %1").arg(watchExp); + break; + } + // 0x000 -> nothing found + if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) { + message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY); + break; + } + // Turn into watch expression: "*(namespace::QWidget*)0x555" + watchExp.replace(sepPos, 1, QLatin1String("*)")); + watchExp.insert(0, QLatin1String("*(")); + watchHandler()->watchExpression(watchExp); + success = true; + } while (false); + if (!success) + showMessage(message, LogWarning); + m_watchPointX = m_watchPointY = 0; +} + +void CdbEngine::watchPoint(const QPoint &p) +{ + m_watchPointX = p.x(); + m_watchPointY = p.y(); + switch (state()) { + case InferiorStopOk: + postWidgetAtCommand(); + break; + case InferiorRunOk: + // "Select Widget to Watch" from a running application is currently not + // supported. It could be implemented via SpecialStopGetWidgetAt-mode, + // but requires some work as not to confuse the engine by state-change notifications + // emitted by the debuggee function call. + showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning); + break; + default: + showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'."). + arg(QString::fromAscii(stateName(state()))), LogWarning); + break; + } +} + +void CdbEngine::postWidgetAtCommand() +{ + QByteArray arguments = QByteArray::number(m_watchPointX); + arguments.append(' '); + arguments.append(QByteArray::number(m_watchPointY)); + postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index bec8579cd0887ff238183eeef592a55eecb98b76..caf74fad626db820aef2f343f2ccddeee35a2153 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -89,6 +89,7 @@ public: virtual void updateWatchData(const WatchData &data, const WatchUpdateFlags & flags = WatchUpdateFlags()); virtual unsigned debuggerCapabilities() const; + virtual void watchPoint(const QPoint &); virtual void setRegisterValue(int regnr, const QString &value); virtual void executeStep(); @@ -150,7 +151,12 @@ private slots: void operateByInstructionTriggered(bool); private: - enum SpecialStopMode { NoSpecialStop, SpecialStopSynchronizeBreakpoints }; + enum SpecialStopMode + { + NoSpecialStop, + SpecialStopSynchronizeBreakpoints, + SpecialStopGetWidgetAt + }; unsigned examineStopReason(const QByteArray &messageIn, QString *message, QString *exceptionBoxMessage); @@ -166,6 +172,7 @@ private: inline bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; } bool canInterruptInferior() const; void syncOperateByInstruction(bool operateByInstruction); + void postWidgetAtCommand(); // Builtin commands void dummyHandler(const CdbBuiltinCommandPtr &); @@ -182,6 +189,7 @@ private: void handleRegisters(const CdbExtensionCommandPtr &reply); void handleModules(const CdbExtensionCommandPtr &reply); void handleMemory(const CdbExtensionCommandPtr &); + void handleWidgetAt(const CdbExtensionCommandPtr &); QString normalizeFileName(const QString &f); void updateLocalVariable(const QByteArray &iname); @@ -215,6 +223,8 @@ private: QByteArray m_extensionMessageBuffer; bool m_sourceStepInto; unsigned m_wX86BreakpointCount; + int m_watchPointX; + int m_watchPointY; }; } // namespace Internal