diff options
author | Dominik Holland <dominik.holland@qt.io> | 2020-08-27 13:10:38 +0200 |
---|---|---|
committer | Dominik Holland <dominik.holland@qt.io> | 2020-09-11 08:11:21 +0000 |
commit | a535196c418a7054c2fb1524ff1df9afb0961166 (patch) | |
tree | 9ebeb0393c0232679f5604469d37ece8045422da | |
parent | e35af70d9888eb7f30846d62eb32e02dbd94b10f (diff) |
Add support for multiple Qt versions in "Load QML Stack"
Enable the functionality for cdb again.
Change-Id: I75405f830dd208cc110d6682a45beedf2f4199cc
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: hjk <hjk@qt.io>
-rw-r--r-- | share/qtcreator/debugger/gdbbridge.py | 22 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp | 81 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbengine.cpp | 14 |
3 files changed, 108 insertions, 9 deletions
diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index c81bca1dd5..824f4c50f7 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -1303,6 +1303,7 @@ class Dumper(DumperBase): gdb.execute('continue') def fetchStack(self, args): + def fromNativePath(string): return string.replace('\\', '/') @@ -1319,7 +1320,11 @@ class Dumper(DumperBase): frame = gdb.newest_frame() ns = self.qtNamespace() needle = self.qtNamespace() + 'QV4::ExecutionEngine' - pat = '%sqt_v4StackTraceForEngine((void*)0x%x)' + pats = [ + '{0}qt_v4StackTraceForEngine((void*)0x{1:x})', + '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext())', + '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext)', + ] done = False while i < limit and frame and not done: block = None @@ -1336,8 +1341,19 @@ class Dumper(DumperBase): dereftype = typeobj.target().unqualified() if dereftype.name == needle: addr = toInteger(value) - expr = pat % (ns, addr) - res = str(gdb.parse_and_eval(expr)) + res = None + for pat in pats: + try: + expr = pat.format(ns, addr) + res = str(gdb.parse_and_eval(expr)) + break + except: + continue + + if res is None: + done = True + break + pos = res.find('"stack=[') if pos != -1: res = res[pos + 8:-2] diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index e9bdd5af92..f4dcde9736 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -1248,13 +1248,84 @@ extern "C" HRESULT CALLBACK qmlstack(CIDebugClient *client, PCSTR argsIn) } // call function to get stack trace. Call with exceptions handled right from // the start assuming this is invoked for crashed applications. - std::ostringstream callStr; - const QtInfo &qtInfo = QtInfo::get(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols())); - callStr << qtInfo.prependQtModule("qt_v4StackTraceForEngine(", QtInfo::Qml) << std::showbase << std::hex - << jsExecutionEngine << std::dec << std::noshowbase << ')'; + // multiple function calls are needed, depending on the used Qt version + // We always start from the latest Qt version + std::ostringstream stringBuilder; std::wstring wOutput; - if (!ExtensionContext::instance().call(callStr.str(), ExtensionContext::CallWithExceptionsHandled, &wOutput, &errorMessage)) + const QtInfo &qtInfo = QtInfo::get(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols())); + do { + stringBuilder << qtInfo.prependQtModule("qt_v4StackTraceForEngine(", QtInfo::Qml) << std::showbase << std::hex + << jsExecutionEngine << std::dec << std::noshowbase << ')'; + if (ExtensionContext::instance().call(stringBuilder.str(), ExtensionContext::CallWithExceptionsHandled, &wOutput, &errorMessage)) + break; + + // < Qt 5.15 + // We need to retrieve the current Context first + std::string currentContextStr; + + // First try calling the currentContext() function + std::wstring callResult; + + stringBuilder.str(""); + stringBuilder << qtInfo.prependQtModule("QV4::ExecutionEngine::currentContext(", QtInfo::Qml) << std::showbase << std::hex + << jsExecutionEngine << std::dec << std::noshowbase << ")"; + if (ExtensionContext::instance().call(stringBuilder.str(), ExtensionContext::CallWithExceptionsHandled, &callResult, &errorMessage)) { + const std::string::size_type sPos = callResult.find(L"struct QV4::ExecutionContext * ") + 31 /*size of pattern*/; + const std::string::size_type sEndPos = callResult.find(L'+'); + + if (sPos == std::string::npos || sEndPos == std::string::npos || sEndPos < sPos) { + errorMessage = "Couldn't parse address from debugger output"; + break; + } + currentContextStr = wStringToString(callResult.substr(sPos, sEndPos - sPos)); + } else { + // < Qt 5.11 ???? + // currentContext is a member, not a function + + stringBuilder.str(""); + stringBuilder << "((QV4::ExecutionEngine*)" << std::showbase << std::hex + << jsExecutionEngine << std::dec << std::noshowbase << ")->currentContext"; + + CIDebugControl *control = ExtensionCommandContext::instance()->control(); + ULONG oldExpressionSyntax; + control->GetExpressionSyntax(&oldExpressionSyntax); + control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS); + + IDebugSymbolGroup2 *symbolGroup = nullptr; + CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols(); + if (FAILED(symbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_ALL, NULL, + &symbolGroup))) + break; + + ULONG index = DEBUG_ANY_ID; + HRESULT hr = symbolGroup->AddSymbol(stringBuilder.str().c_str(), &index); + control->SetExpressionSyntax(oldExpressionSyntax); + if (SUCCEEDED(hr)) { + ULONG64 address = 0; + HRESULT hr = symbolGroup->GetSymbolOffset(index, &address); + if (SUCCEEDED(hr)) { + ExtensionCommandContext::instance()->dataSpaces()->ReadPointersVirtual(1, address, &address); + stringBuilder.str(""); + stringBuilder << std::showbase << std::hex << address; + currentContextStr = stringBuilder.str(); + } + } + } + + if (currentContextStr.empty()) { + errorMessage = "Failed to retrieve currenContext from QML engine"; + break; + } + + stringBuilder.str(""); + stringBuilder << qtInfo.prependQtModule("qt_v4StackTrace(", QtInfo::Qml) << currentContextStr << ')'; + if (ExtensionContext::instance().call(stringBuilder.str(), ExtensionContext::CallWithExceptionsHandled, &wOutput, &errorMessage)) + break; + } while (false); + + if (wOutput.empty()) break; + // extract GDBMI info from call const std::string::size_type sPos = wOutput.find(L"stack=["); const std::string::size_type sEndPos = wOutput.rfind(L']'); diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 211545a708..8596209a70 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -41,6 +41,7 @@ #include <debugger/debuggertooltipmanager.h> #include <debugger/disassembleragent.h> #include <debugger/disassemblerlines.h> +#include <debugger/enginemanager.h> #include <debugger/memoryagent.h> #include <debugger/moduleshandler.h> #include <debugger/registerhandler.h> @@ -732,7 +733,8 @@ bool CdbEngine::hasCapability(unsigned cap) const | CreateFullBacktraceCapability | OperateByInstructionCapability | RunToLineCapability - | MemoryAddressCapability); + | MemoryAddressCapability + | AdditionalQmlStackCapability); } void CdbEngine::executeStepIn(bool byInstruction) @@ -2630,6 +2632,8 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = nullptr) frame.language = QmlLanguage; } frame.function = frameMi["function"].data(); + if (frame.function.isEmpty()) + frame.function = frameMi["func"].data(); // GDB's *stopped messages frame.module = frameMi["from"].data(); frame.context = frameMi["context"].data(); frame.address = frameMi["address"].data().toULongLong(nullptr, 16); @@ -2687,6 +2691,14 @@ unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto) void CdbEngine::loadAdditionalQmlStack() { + // Creating a qml stack while the QmlEngine is stopped results in a frozen inferior. + const auto engineList = EngineManager::engines(); + for (DebuggerEngine *engine : engineList) { + if (engine->objectName() == "QmlEngine" && engine->state() == Debugger::InferiorStopOk) { + showMessage("Can't create a QML stack trace while the QML Debugger is in the Stopped state", StatusBar); + return; + } + } runCommand({"qmlstack", ExtensionCommand, CB(handleAdditionalQmlStack)}); } |