forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -1303,6 +1303,7 @@ class Dumper(DumperBase):
|
|||||||
gdb.execute('continue')
|
gdb.execute('continue')
|
||||||
|
|
||||||
def fetchStack(self, args):
|
def fetchStack(self, args):
|
||||||
|
|
||||||
def fromNativePath(string):
|
def fromNativePath(string):
|
||||||
return string.replace('\\', '/')
|
return string.replace('\\', '/')
|
||||||
|
|
||||||
@@ -1319,7 +1320,11 @@ class Dumper(DumperBase):
|
|||||||
frame = gdb.newest_frame()
|
frame = gdb.newest_frame()
|
||||||
ns = self.qtNamespace()
|
ns = self.qtNamespace()
|
||||||
needle = self.qtNamespace() + 'QV4::ExecutionEngine'
|
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
|
done = False
|
||||||
while i < limit and frame and not done:
|
while i < limit and frame and not done:
|
||||||
block = None
|
block = None
|
||||||
@@ -1336,8 +1341,19 @@ class Dumper(DumperBase):
|
|||||||
dereftype = typeobj.target().unqualified()
|
dereftype = typeobj.target().unqualified()
|
||||||
if dereftype.name == needle:
|
if dereftype.name == needle:
|
||||||
addr = toInteger(value)
|
addr = toInteger(value)
|
||||||
expr = pat % (ns, addr)
|
res = None
|
||||||
res = str(gdb.parse_and_eval(expr))
|
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=[')
|
pos = res.find('"stack=[')
|
||||||
if pos != -1:
|
if pos != -1:
|
||||||
res = res[pos + 8:-2]
|
res = res[pos + 8:-2]
|
||||||
|
|||||||
@@ -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
|
// call function to get stack trace. Call with exceptions handled right from
|
||||||
// the start assuming this is invoked for crashed applications.
|
// the start assuming this is invoked for crashed applications.
|
||||||
std::ostringstream callStr;
|
// multiple function calls are needed, depending on the used Qt version
|
||||||
const QtInfo &qtInfo = QtInfo::get(SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()));
|
// We always start from the latest Qt version
|
||||||
callStr << qtInfo.prependQtModule("qt_v4StackTraceForEngine(", QtInfo::Qml) << std::showbase << std::hex
|
std::ostringstream stringBuilder;
|
||||||
<< jsExecutionEngine << std::dec << std::noshowbase << ')';
|
|
||||||
std::wstring wOutput;
|
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;
|
break;
|
||||||
|
|
||||||
// extract GDBMI info from call
|
// extract GDBMI info from call
|
||||||
const std::string::size_type sPos = wOutput.find(L"stack=[");
|
const std::string::size_type sPos = wOutput.find(L"stack=[");
|
||||||
const std::string::size_type sEndPos = wOutput.rfind(L']');
|
const std::string::size_type sEndPos = wOutput.rfind(L']');
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include <debugger/debuggertooltipmanager.h>
|
#include <debugger/debuggertooltipmanager.h>
|
||||||
#include <debugger/disassembleragent.h>
|
#include <debugger/disassembleragent.h>
|
||||||
#include <debugger/disassemblerlines.h>
|
#include <debugger/disassemblerlines.h>
|
||||||
|
#include <debugger/enginemanager.h>
|
||||||
#include <debugger/memoryagent.h>
|
#include <debugger/memoryagent.h>
|
||||||
#include <debugger/moduleshandler.h>
|
#include <debugger/moduleshandler.h>
|
||||||
#include <debugger/registerhandler.h>
|
#include <debugger/registerhandler.h>
|
||||||
@@ -732,7 +733,8 @@ bool CdbEngine::hasCapability(unsigned cap) const
|
|||||||
| CreateFullBacktraceCapability
|
| CreateFullBacktraceCapability
|
||||||
| OperateByInstructionCapability
|
| OperateByInstructionCapability
|
||||||
| RunToLineCapability
|
| RunToLineCapability
|
||||||
| MemoryAddressCapability);
|
| MemoryAddressCapability
|
||||||
|
| AdditionalQmlStackCapability);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CdbEngine::executeStepIn(bool byInstruction)
|
void CdbEngine::executeStepIn(bool byInstruction)
|
||||||
@@ -2630,6 +2632,8 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = nullptr)
|
|||||||
frame.language = QmlLanguage;
|
frame.language = QmlLanguage;
|
||||||
}
|
}
|
||||||
frame.function = frameMi["function"].data();
|
frame.function = frameMi["function"].data();
|
||||||
|
if (frame.function.isEmpty())
|
||||||
|
frame.function = frameMi["func"].data(); // GDB's *stopped messages
|
||||||
frame.module = frameMi["from"].data();
|
frame.module = frameMi["from"].data();
|
||||||
frame.context = frameMi["context"].data();
|
frame.context = frameMi["context"].data();
|
||||||
frame.address = frameMi["address"].data().toULongLong(nullptr, 16);
|
frame.address = frameMi["address"].data().toULongLong(nullptr, 16);
|
||||||
@@ -2687,6 +2691,14 @@ unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
|
|||||||
|
|
||||||
void CdbEngine::loadAdditionalQmlStack()
|
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)});
|
runCommand({"qmlstack", ExtensionCommand, CB(handleAdditionalQmlStack)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user