diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp index 2419f7cebd4..2afe0c3bcb0 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.cpp +++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp @@ -29,6 +29,7 @@ #include "extensioncontext.h" #include "symbolgroup.h" +#include "symbolgroupvalue.h" #include "eventcallback.h" #include "outputcallback.h" #include "stringutils.h" @@ -189,6 +190,80 @@ ULONG ExtensionContext::executionStatus() const return (m_control && SUCCEEDED(m_control->GetExecutionStatus(&ex))) ? ex : ULONG(0); } +// Helpers for finding the address of the JS execution context in +// case of a QML crash: Find module +static std::string findModule(CIDebugSymbols *syms, + const std::string &name, + std::string *errorMessage) +{ + const Modules mods = getModules(syms, errorMessage); + const size_t count = mods.size(); + for (size_t m = 0; m < count; ++m) + if (!mods.at(m).name.compare(0, name.size(), name)) + return mods.at(m).name; + return std::string(); +} + +// Try to find a JS execution context passed as parameter in a complete stack dump (kp) +static ULONG64 jsExecutionContextFromStackTrace(const std::wstring &stack) +{ + // Search for "QV4::ExecutionContext * - varying variable names - 0x...[,)]" + const wchar_t needle[] = L"struct QV4::ExecutionContext * "; // .. varying variable names .. 0x... + const std::string::size_type varPos = stack.find(needle); + if (varPos == std::string::npos) + return 0; + const std::string::size_type varEnd = varPos + sizeof(needle) / sizeof(wchar_t) - 1; + std::string::size_type numPos = stack.find(L"0x", varEnd); + if (numPos == std::string::npos || numPos > (varEnd + 20)) + return 0; + numPos += 2; + const std::string::size_type endPos = stack.find_first_of(L",)", numPos); + if (endPos == std::string::npos) + return 0; + // Fix hex values: (0x)000000f5`cecae5b0 -> (0x)000000f5cecae5b0 + std::wstring address = stack.substr(numPos, endPos - numPos); + if (address.size() > 8 && address.at(8) == L'`') + address.erase(8, 1); + std::wistringstream str(address); + ULONG64 result; + str >> std::hex >> result; + return str.fail() ? 0 : result; +} + +// Try to find address of jsExecutionContext by looking at the +// stack trace in case QML is loaded. +ULONG64 ExtensionContext::jsExecutionContext(ExtensionCommandContext &exc, + std::string *errorMessage) +{ + + const QtInfo &qtInfo = QtInfo::get(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols())); + static const std::string qmlModule = + findModule(exc.symbols(), qtInfo.moduleName(QtInfo::Qml), errorMessage); + if (qmlModule.empty()) { + if (errorMessage->empty()) + *errorMessage = "QML not loaded"; + return 0; + } + // Retrieve full stack (costly) and try to find a JS execution context passed as parameter + startRecordingOutput(); + StateNotificationBlocker blocker(this); + const HRESULT hr = m_control->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "kp", DEBUG_EXECUTE_ECHO); + if (FAILED(hr)) { + stopRecordingOutput(); + *errorMessage = msgDebugEngineComFailed("Execute", hr); + return 0; + } + const std::wstring fullStackTrace = stopRecordingOutput(); + if (fullStackTrace.empty()) { + *errorMessage = "Unable to obtain stack (output redirection in place?)"; + return 0; + } + const ULONG64 result = jsExecutionContextFromStackTrace(fullStackTrace); + if (!result) + *errorMessage = "JS ExecutionContext address not found in stack"; + return result; +} + // Complete stop parameters with common parameters and report static inline ExtensionContext::StopReasonMap completeStopReasons(CIDebugClient *client, ExtensionContext::StopReasonMap stopReasons, ULONG ex) diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h index f4f0cac43ea..8b61c34908c 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.h +++ b/src/libs/qtcreatorcdbext/extensioncontext.h @@ -39,6 +39,7 @@ class LocalsSymbolGroup; class WatchesSymbolGroup; class OutputCallback; +class ExtensionCommandContext; // Global parameters class Parameters @@ -121,6 +122,8 @@ public: const Parameters ¶meters() const { return m_parameters; } Parameters ¶meters() { return m_parameters; } + ULONG64 jsExecutionContext(ExtensionCommandContext &exc, std::string *errorMessage); + bool stateNotification() const { return m_stateNotification; } void setStateNotification(bool s) { m_stateNotification = s; } diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def index db92e4750f4..c9e9fe28d80 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def @@ -21,6 +21,7 @@ expression shutdownex test stack +qmlstack addwatch widgetat breakpoints diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 3889b2777f2..eced0c95040 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -104,6 +104,7 @@ enum Command { CmdMemory, CmdExpression, CmdStack, + CmdQmlStack, CmdShutdownex, CmdAddWatch, CmdWidgetAt, @@ -167,12 +168,13 @@ static const CommandDescription commandDescriptions[] = { {"memory","Prints memory contents in Base64 encoding.","[-t token]