aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Holland <dominik.holland@qt.io>2020-08-27 13:10:38 +0200
committerDominik Holland <dominik.holland@qt.io>2020-09-11 08:11:21 +0000
commita535196c418a7054c2fb1524ff1df9afb0961166 (patch)
tree9ebeb0393c0232679f5604469d37ece8045422da
parente35af70d9888eb7f30846d62eb32e02dbd94b10f (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.py22
-rw-r--r--src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp81
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp14
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)});
}