forked from qt-creator/qt-creator
debugger: start splitting off python and non-python specific bits
This commit is contained in:
761
src/plugins/debugger/gdb/classicgdbengine.cpp
Normal file
761
src/plugins/debugger/gdb/classicgdbengine.cpp
Normal file
@@ -0,0 +1,761 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "gdbengine.h"
|
||||||
|
|
||||||
|
#include "abstractgdbadapter.h"
|
||||||
|
#include "debuggeractions.h"
|
||||||
|
#include "debuggerstringutils.h"
|
||||||
|
|
||||||
|
#include "stackhandler.h"
|
||||||
|
#include "watchhandler.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
|
||||||
|
|
||||||
|
#define PRECONDITION QTC_ASSERT(!hasPython(), /**/)
|
||||||
|
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG_PENDING 1
|
||||||
|
//#define DEBUG_SUBITEM 1
|
||||||
|
|
||||||
|
#if DEBUG_PENDING
|
||||||
|
# define PENDING_DEBUG(s) qDebug() << s
|
||||||
|
#else
|
||||||
|
# define PENDING_DEBUG(s)
|
||||||
|
#endif
|
||||||
|
#define PENDING_DEBUGX(s) qDebug() << s
|
||||||
|
|
||||||
|
namespace Debugger {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
// reads a MI-encoded item frome the consolestream
|
||||||
|
static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
|
||||||
|
{
|
||||||
|
GdbMi output = response.data.findChild("consolestreamoutput");
|
||||||
|
QByteArray out = output.data();
|
||||||
|
|
||||||
|
int markerPos = out.indexOf('"') + 1; // position of 'success marker'
|
||||||
|
if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f'
|
||||||
|
// custom dumper produced no output
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out = out.mid(markerPos + 1);
|
||||||
|
out = out.left(out.lastIndexOf('"'));
|
||||||
|
// optimization: dumper output never needs real C unquoting
|
||||||
|
out.replace('\\', "");
|
||||||
|
|
||||||
|
contents->fromStringMultiple(out);
|
||||||
|
//qDebug() << "CONTENTS" << contents->toString(true);
|
||||||
|
return contents->isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static double getDumperVersion(const GdbMi &contents)
|
||||||
|
{
|
||||||
|
const GdbMi dumperVersionG = contents.findChild("dumperversion");
|
||||||
|
if (dumperVersionG.type() != GdbMi::Invalid) {
|
||||||
|
bool ok;
|
||||||
|
const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
|
||||||
|
if (ok)
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GdbEngine::updateLocalsClassic(const QVariant &cookie)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
m_processedNames.clear();
|
||||||
|
|
||||||
|
//PENDING_DEBUG("\nRESET PENDING");
|
||||||
|
//m_toolTipCache.clear();
|
||||||
|
m_toolTipExpression.clear();
|
||||||
|
manager()->watchHandler()->beginCycle();
|
||||||
|
|
||||||
|
// Asynchronous load of injected library, initialize in first stop
|
||||||
|
if (m_dumperInjectionLoad && m_debuggingHelperState == DebuggingHelperLoadTried
|
||||||
|
&& m_dumperHelper.typeCount() == 0
|
||||||
|
&& inferiorPid() > 0)
|
||||||
|
tryQueryDebuggingHelpersClassic();
|
||||||
|
|
||||||
|
QByteArray level = QByteArray::number(currentFrame());
|
||||||
|
// '2' is 'list with type and value'
|
||||||
|
QByteArray cmd = "-stack-list-arguments 2 " + level + ' ' + level;
|
||||||
|
postCommand(cmd, WatchUpdate,
|
||||||
|
CB(handleStackListArgumentsClassic));
|
||||||
|
// '2' is 'list with type and value'
|
||||||
|
postCommand("-stack-list-locals 2", WatchUpdate,
|
||||||
|
CB(handleStackListLocalsClassic), cookie); // stage 2/2
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString msgRetrievingWatchData(int pending)
|
||||||
|
{
|
||||||
|
return GdbEngine::tr("Retrieving data for watch view (%n requests pending)...", 0, pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren)
|
||||||
|
{
|
||||||
|
Q_UNUSED(dumpChildren)
|
||||||
|
QByteArray type = data.type.toLatin1();
|
||||||
|
QByteArray cmd;
|
||||||
|
|
||||||
|
if (type == "QString" || type.endsWith("::QString"))
|
||||||
|
cmd = "qdumpqstring (&(" + data.exp + "))";
|
||||||
|
else if (type == "QStringList" || type.endsWith("::QStringList"))
|
||||||
|
cmd = "qdumpqstringlist (&(" + data.exp + "))";
|
||||||
|
|
||||||
|
QVariant var;
|
||||||
|
var.setValue(data);
|
||||||
|
postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3Classic), var);
|
||||||
|
|
||||||
|
showStatusMessage(msgRetrievingWatchData(m_pendingRequests + 1), 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::runDebuggingHelperClassic(const WatchData &data0, bool dumpChildren)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (m_debuggingHelperState != DebuggingHelperAvailable) {
|
||||||
|
runDirectDebuggingHelperClassic(data0, dumpChildren);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WatchData data = data0;
|
||||||
|
|
||||||
|
// Avoid endless loops created by faulty dumpers.
|
||||||
|
QByteArray processedName = QByteArray::number(dumpChildren) + '-' + data.iname;
|
||||||
|
if (m_processedNames.contains(processedName)) {
|
||||||
|
gdbInputAvailable(LogStatus,
|
||||||
|
_("<Breaking endless loop for " + data.iname + ">"));
|
||||||
|
data.setAllUnneeded();
|
||||||
|
data.setValue(_("<unavailable>"));
|
||||||
|
data.setHasChildren(false);
|
||||||
|
insertData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_processedNames.insert(processedName);
|
||||||
|
|
||||||
|
QByteArray params;
|
||||||
|
QStringList extraArgs;
|
||||||
|
const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
|
||||||
|
m_dumperHelper.evaluationParameters(data, td, QtDumperHelper::GdbDebugger, ¶ms, &extraArgs);
|
||||||
|
|
||||||
|
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
|
||||||
|
//int protocol = data.iname.startsWith("watch") ? 3 : 2;
|
||||||
|
const int protocol = 2;
|
||||||
|
//int protocol = isDisplayedIName(data.iname) ? 3 : 2;
|
||||||
|
|
||||||
|
QByteArray addr;
|
||||||
|
if (data.addr.startsWith("0x"))
|
||||||
|
addr = "(void*)" + data.addr;
|
||||||
|
else if (data.exp.isEmpty()) // happens e.g. for QAbstractItem
|
||||||
|
addr = "0";
|
||||||
|
else
|
||||||
|
addr = "&(" + data.exp + ')';
|
||||||
|
|
||||||
|
sendWatchParameters(params);
|
||||||
|
|
||||||
|
QString cmd;
|
||||||
|
QTextStream(&cmd) << "call " << "(void*)qDumpObjectData440(" <<
|
||||||
|
protocol << ",0," << addr << ',' << (dumpChildren ? "1" : "0")
|
||||||
|
<< ',' << extraArgs.join(QString(_c(','))) << ')';
|
||||||
|
|
||||||
|
postCommand(cmd.toLatin1(), WatchUpdate | NonCriticalResponse);
|
||||||
|
|
||||||
|
showStatusMessage(msgRetrievingWatchData(m_pendingRequests + 1), 10000);
|
||||||
|
|
||||||
|
// retrieve response
|
||||||
|
postCommand("p (char*)&qDumpOutBuffer", WatchUpdate,
|
||||||
|
CB(handleDebuggingHelperValue2Classic), qVariantFromValue(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::createGdbVariableClassic(const WatchData &data)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (data.iname == "local.flist.0") {
|
||||||
|
int i = 1;
|
||||||
|
Q_UNUSED(i);
|
||||||
|
}
|
||||||
|
postCommand("-var-delete \"" + data.iname + '"', WatchUpdate);
|
||||||
|
QByteArray exp = data.exp;
|
||||||
|
if (exp.isEmpty() && data.addr.startsWith("0x"))
|
||||||
|
exp = "*(" + gdbQuoteTypes(data.type).toLatin1() + "*)" + data.addr;
|
||||||
|
QVariant val = QVariant::fromValue<WatchData>(data);
|
||||||
|
postCommand("-var-create \"" + data.iname + "\" * \"" + exp + '"',
|
||||||
|
WatchUpdate, CB(handleVarCreate), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::updateSubItemClassic(const WatchData &data0)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
WatchData data = data0;
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM:" << data.toString();
|
||||||
|
#endif
|
||||||
|
QTC_ASSERT(data.isValid(), return);
|
||||||
|
|
||||||
|
// in any case we need the type first
|
||||||
|
if (data.isTypeNeeded()) {
|
||||||
|
// This should only happen if we don't have a variable yet.
|
||||||
|
// Let's play safe, though.
|
||||||
|
if (!data.variable.isEmpty()) {
|
||||||
|
// Update: It does so for out-of-scope watchers.
|
||||||
|
#if 1
|
||||||
|
qDebug() << "FIXME: GdbEngine::updateSubItem:"
|
||||||
|
<< data.toString() << "should not happen";
|
||||||
|
#else
|
||||||
|
data.setType(WatchData::msgNotInScope());
|
||||||
|
data.setValue(WatchData::msgNotInScope());
|
||||||
|
data.setHasChildren(false);
|
||||||
|
insertData(data);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// The WatchVarCreate handler will receive type information
|
||||||
|
// and re-insert a WatchData item with correct type, so
|
||||||
|
// we will not re-enter this bit.
|
||||||
|
// FIXME: Concurrency issues?
|
||||||
|
createGdbVariableClassic(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should have a type now. this is relied upon further below
|
||||||
|
QTC_ASSERT(!data.type.isEmpty(), return);
|
||||||
|
|
||||||
|
// a common case that can be easily solved
|
||||||
|
if (data.isChildrenNeeded() && isPointerType(data.type)
|
||||||
|
&& !hasDebuggingHelperForType(data.type)) {
|
||||||
|
// We sometimes know what kind of children pointers have
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "IT'S A POINTER";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (theDebuggerBoolSetting(AutoDerefPointers)) {
|
||||||
|
// Try automatic dereferentiation
|
||||||
|
data.exp = "(*(" + data.exp + "))";
|
||||||
|
data.type = data.type + _("."); // FIXME: fragile HACK to avoid recursion
|
||||||
|
insertData(data);
|
||||||
|
} else {
|
||||||
|
data.setChildrenUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
WatchData data1;
|
||||||
|
data1.iname = data.iname + ".*";
|
||||||
|
data1.name = QLatin1Char('*') + data.name;
|
||||||
|
data1.exp = "(*(" + data.exp + "))";
|
||||||
|
data1.type = stripPointerType(data.type);
|
||||||
|
data1.setValueNeeded();
|
||||||
|
data1.setChildrenUnneeded();
|
||||||
|
insertData(data1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isValueNeeded() && hasDebuggingHelperForType(data.type)) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
|
||||||
|
#endif
|
||||||
|
runDebuggingHelperClassic(data,
|
||||||
|
manager()->watchHandler()->isExpandedIName(data.iname));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (data.isValueNeeded() && data.exp.isEmpty()) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
|
||||||
|
#endif
|
||||||
|
data.setError("<no expression given>");
|
||||||
|
insertData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (data.isValueNeeded() && data.variable.isEmpty()) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
|
||||||
|
#endif
|
||||||
|
createGdbVariableClassic(data);
|
||||||
|
// the WatchVarCreate handler will re-insert a WatchData
|
||||||
|
// item, with valueNeeded() set.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isValueNeeded()) {
|
||||||
|
QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: VALUE";
|
||||||
|
#endif
|
||||||
|
QByteArray cmd = "-var-evaluate-expression \"" + data.iname + '"';
|
||||||
|
postCommand(cmd, WatchUpdate, CB(handleEvaluateExpression),
|
||||||
|
QVariant::fromValue(data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
|
||||||
|
#endif
|
||||||
|
runDebuggingHelperClassic(data, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isChildrenNeeded() && data.variable.isEmpty()) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
|
||||||
|
#endif
|
||||||
|
createGdbVariableClassic(data);
|
||||||
|
// the WatchVarCreate handler will re-insert a WatchData
|
||||||
|
// item, with childrenNeeded() set.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isChildrenNeeded()) {
|
||||||
|
QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
|
||||||
|
QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
|
||||||
|
postCommand(cmd, WatchUpdate, CB(handleVarListChildren), QVariant::fromValue(data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.isHasChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
|
||||||
|
#endif
|
||||||
|
runDebuggingHelperClassic(data,
|
||||||
|
manager()->watchHandler()->isExpandedIName(data.iname));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#if !X
|
||||||
|
if (data.isHasChildrenNeeded() && data.variable.isEmpty()) {
|
||||||
|
#if DEBUG_SUBITEM
|
||||||
|
qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
|
||||||
|
#endif
|
||||||
|
createGdbVariableClassic(data);
|
||||||
|
// the WatchVarCreate handler will re-insert a WatchData
|
||||||
|
// item, with childrenNeeded() set.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
if (data.isHasChildrenNeeded()) {
|
||||||
|
QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
|
||||||
|
QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
|
||||||
|
postCommand(cmd, Discardable,
|
||||||
|
CB(handleVarListChildren), QVariant::fromValue(data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "FIXME: UPDATE SUBITEM:" << data.toString();
|
||||||
|
QTC_ASSERT(false, return);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
WatchData data = response.cookie.value<WatchData>();
|
||||||
|
QTC_ASSERT(data.isValid(), return);
|
||||||
|
|
||||||
|
// The real dumper might have aborted without giving any answers.
|
||||||
|
// Remove traces of the question, too.
|
||||||
|
if (m_cookieForToken.contains(response.token - 1)) {
|
||||||
|
m_cookieForToken.remove(response.token - 1);
|
||||||
|
debugMessage(_("DETECTING LOST COMMAND %1").arg(response.token - 1));
|
||||||
|
--m_pendingRequests;
|
||||||
|
data.setError(WatchData::msgNotInScope());
|
||||||
|
insertData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//qDebug() << "CUSTOM VALUE RESULT:" << response.toString();
|
||||||
|
//qDebug() << "FOR DATA:" << data.toString() << response.resultClass;
|
||||||
|
if (response.resultClass != GdbResultDone) {
|
||||||
|
qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA:" << data.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GdbMi contents;
|
||||||
|
if (!parseConsoleStream(response, &contents)) {
|
||||||
|
data.setError(WatchData::msgNotInScope());
|
||||||
|
insertData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setWatchDataType(data, response.data.findChild("type"));
|
||||||
|
setWatchDataDisplayedType(data, response.data.findChild("displaytype"));
|
||||||
|
QList<WatchData> list;
|
||||||
|
handleChildren(data, contents, &list);
|
||||||
|
//for (int i = 0; i != list.size(); ++i)
|
||||||
|
// qDebug() << "READ: " << list.at(i).toString();
|
||||||
|
manager()->watchHandler()->insertBulkData(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
if (response.resultClass == GdbResultDone) {
|
||||||
|
WatchData data = response.cookie.value<WatchData>();
|
||||||
|
QByteArray out = response.data.findChild("consolestreamoutput").data();
|
||||||
|
while (out.endsWith(' ') || out.endsWith('\n'))
|
||||||
|
out.chop(1);
|
||||||
|
QList<QByteArray> list = out.split(' ');
|
||||||
|
//qDebug() << "RECEIVED" << response.toString() << "FOR" << data0.toString()
|
||||||
|
// << " STREAM:" << out;
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
//: Value for variable
|
||||||
|
data.setError(WatchData::msgNotInScope());
|
||||||
|
data.setAllUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
} else if (data.type == __("QString")
|
||||||
|
|| data.type.endsWith(__("::QString"))) {
|
||||||
|
QList<QByteArray> list = out.split(' ');
|
||||||
|
QString str;
|
||||||
|
int l = out.isEmpty() ? 0 : list.size();
|
||||||
|
for (int i = 0; i < l; ++i)
|
||||||
|
str.append(list.at(i).toInt());
|
||||||
|
data.setValue(_c('"') + str + _c('"'));
|
||||||
|
data.setHasChildren(false);
|
||||||
|
data.setAllUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
} else if (data.type == __("QStringList")
|
||||||
|
|| data.type.endsWith(__("::QStringList"))) {
|
||||||
|
if (out.isEmpty()) {
|
||||||
|
data.setValue(tr("<0 items>"));
|
||||||
|
data.setHasChildren(false);
|
||||||
|
data.setAllUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
} else {
|
||||||
|
int l = list.size();
|
||||||
|
//: In string list
|
||||||
|
data.setValue(tr("<%n items>", 0, l));
|
||||||
|
data.setHasChildren(!list.empty());
|
||||||
|
data.setAllUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
for (int i = 0; i < l; ++i) {
|
||||||
|
WatchData data1;
|
||||||
|
data1.name = _("[%1]").arg(i);
|
||||||
|
data1.type = data.type.left(data.type.size() - 4);
|
||||||
|
data1.iname = data.iname + '.' + QByteArray::number(i);
|
||||||
|
data1.addr = list.at(i);
|
||||||
|
data1.exp = "((" + gdbQuoteTypes(data1.type).toLatin1() + "*)" + data1.addr + ")";
|
||||||
|
data1.setHasChildren(false);
|
||||||
|
data1.setValueNeeded();
|
||||||
|
QByteArray cmd = "qdumpqstring (" + data1.exp + ')';
|
||||||
|
QVariant var;
|
||||||
|
var.setValue(data1);
|
||||||
|
postCommand(cmd, WatchUpdate,
|
||||||
|
CB(handleDebuggingHelperValue3Classic), var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//: Value for variable
|
||||||
|
data.setError(WatchData::msgNotInScope());
|
||||||
|
data.setAllUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WatchData data = response.cookie.value<WatchData>();
|
||||||
|
data.setError(WatchData::msgNotInScope());
|
||||||
|
data.setAllUnneeded();
|
||||||
|
insertData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::tryLoadDebuggingHelpersClassic()
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (isSynchroneous())
|
||||||
|
return;
|
||||||
|
switch (m_debuggingHelperState) {
|
||||||
|
case DebuggingHelperUninitialized:
|
||||||
|
break;
|
||||||
|
case DebuggingHelperLoadTried:
|
||||||
|
tryQueryDebuggingHelpersClassic();
|
||||||
|
return;
|
||||||
|
case DebuggingHelperAvailable:
|
||||||
|
case DebuggingHelperUnavailable:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
|
||||||
|
// Load at least gdb macro based dumpers.
|
||||||
|
QFile file(_(":/gdb/gdbmacros.txt"));
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
QByteArray contents = file.readAll();
|
||||||
|
m_debuggingHelperState = DebuggingHelperLoadTried;
|
||||||
|
postCommand(contents);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_dumperInjectionLoad && inferiorPid() <= 0) // Need PID to inject
|
||||||
|
return;
|
||||||
|
|
||||||
|
PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
|
||||||
|
m_debuggingHelperState = DebuggingHelperUnavailable;
|
||||||
|
if (!checkDebuggingHelpersClassic())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_debuggingHelperState = DebuggingHelperLoadTried;
|
||||||
|
QByteArray dlopenLib;
|
||||||
|
if (startParameters().startMode == StartRemote)
|
||||||
|
dlopenLib = startParameters().remoteDumperLib.toLocal8Bit();
|
||||||
|
else
|
||||||
|
dlopenLib = manager()->qtDumperLibraryName().toLocal8Bit();
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
if (m_dumperInjectionLoad) {
|
||||||
|
/// Launch asynchronous remote thread to load.
|
||||||
|
SharedLibraryInjector injector(inferiorPid());
|
||||||
|
QString errorMessage;
|
||||||
|
const QString dlopenLibString = _(dlopenLib);
|
||||||
|
if (injector.remoteInject(dlopenLibString, false, &errorMessage)) {
|
||||||
|
debugMessage(_("Dumper injection loading triggered (%1)...").
|
||||||
|
arg(dlopenLibString));
|
||||||
|
} else {
|
||||||
|
debugMessage(_("Dumper loading (%1) failed: %2").
|
||||||
|
arg(dlopenLibString, errorMessage));
|
||||||
|
debugMessage(errorMessage);
|
||||||
|
manager()->showQtDumperLibraryWarning(errorMessage);
|
||||||
|
m_debuggingHelperState = DebuggingHelperUnavailable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugMessage(_("Loading dumpers via debugger call (%1)...").
|
||||||
|
arg(_(dlopenLib)));
|
||||||
|
postCommand("sharedlibrary .*"); // for LoadLibraryA
|
||||||
|
//postCommand("handle SIGSEGV pass stop print");
|
||||||
|
//postCommand("set unwindonsignal off");
|
||||||
|
postCommand("call LoadLibraryA(\"" + GdbMi::escapeCString(dlopenLib) + "\")",
|
||||||
|
CB(handleDebuggingHelperSetup));
|
||||||
|
postCommand("sharedlibrary " + dotEscape(dlopenLib));
|
||||||
|
}
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
//postCommand("sharedlibrary libc"); // for malloc
|
||||||
|
//postCommand("sharedlibrary libdl"); // for dlopen
|
||||||
|
postCommand("call (void)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
|
||||||
|
+ "\", " STRINGIFY(RTLD_NOW) ")",
|
||||||
|
CB(handleDebuggingHelperSetup));
|
||||||
|
//postCommand("sharedlibrary " + dotEscape(dlopenLib));
|
||||||
|
#else
|
||||||
|
//postCommand("p dlopen");
|
||||||
|
postCommand("sharedlibrary libc"); // for malloc
|
||||||
|
postCommand("sharedlibrary libdl"); // for dlopen
|
||||||
|
postCommand("call (void*)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
|
||||||
|
+ "\", " STRINGIFY(RTLD_NOW) ")",
|
||||||
|
CB(handleDebuggingHelperSetup));
|
||||||
|
// some older systems like CentOS 4.6 prefer this:
|
||||||
|
postCommand("call (void*)__dlopen(\"" + GdbMi::escapeCString(dlopenLib)
|
||||||
|
+ "\", " STRINGIFY(RTLD_NOW) ")",
|
||||||
|
CB(handleDebuggingHelperSetup));
|
||||||
|
postCommand("sharedlibrary " + dotEscape(dlopenLib));
|
||||||
|
#endif
|
||||||
|
if (!m_dumperInjectionLoad)
|
||||||
|
tryQueryDebuggingHelpersClassic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::tryQueryDebuggingHelpersClassic()
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
// retrieve list of dumpable classes
|
||||||
|
postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
|
||||||
|
postCommand("p (char*)&qDumpOutBuffer",
|
||||||
|
CB(handleQueryDebuggingHelperClassic));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::recheckDebuggingHelperAvailabilityClassic()
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
|
||||||
|
// retrieve list of dumpable classes
|
||||||
|
postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
|
||||||
|
postCommand("p (char*)&qDumpOutBuffer",
|
||||||
|
CB(handleQueryDebuggingHelperClassic));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from CoreAdapter and AttachAdapter
|
||||||
|
void GdbEngine::updateAllClassic()
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
PENDING_DEBUG("UPDATING ALL\n");
|
||||||
|
QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
|
||||||
|
tryLoadDebuggingHelpersClassic();
|
||||||
|
reloadModulesInternal();
|
||||||
|
postCommand("-stack-list-frames", WatchUpdate,
|
||||||
|
CB(handleStackListFrames),
|
||||||
|
QVariant::fromValue<StackCookie>(StackCookie(false, true)));
|
||||||
|
manager()->stackHandler()->setCurrentIndex(0);
|
||||||
|
if (supportsThreads())
|
||||||
|
postCommand("-thread-list-ids", WatchUpdate, CB(handleStackListThreads), 0);
|
||||||
|
manager()->reloadRegisters();
|
||||||
|
updateLocals();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::setDebugDebuggingHelpersClassic(const QVariant &on)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (on.toBool()) {
|
||||||
|
debugMessage(_("SWITCHING ON DUMPER DEBUGGING"));
|
||||||
|
postCommand("set unwindonsignal off");
|
||||||
|
m_manager->breakByFunction(_("qDumpObjectData440"));
|
||||||
|
//updateLocals();
|
||||||
|
} else {
|
||||||
|
debugMessage(_("SWITCHING OFF DUMPER DEBUGGING"));
|
||||||
|
postCommand("set unwindonsignal on");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::setDebuggingHelperStateClassic(DebuggingHelperState s)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
m_debuggingHelperState = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GdbEngine::handleStackListArgumentsClassic(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
// stage 1/2
|
||||||
|
|
||||||
|
// Linux:
|
||||||
|
// 12^done,stack-args=
|
||||||
|
// [frame={level="0",args=[
|
||||||
|
// {name="argc",type="int",value="1"},
|
||||||
|
// {name="argv",type="char **",value="(char **) 0x7..."}]}]
|
||||||
|
// Mac:
|
||||||
|
// 78^done,stack-args=
|
||||||
|
// {frame={level="0",args={
|
||||||
|
// varobj=
|
||||||
|
// {exp="this",value="0x38a2fab0",name="var21",numchild="3",
|
||||||
|
// type="CurrentDocumentFind * const",typecode="PTR",
|
||||||
|
// dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
|
||||||
|
// block_end_addr="0x3938eb2d"},
|
||||||
|
// varobj=
|
||||||
|
// {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
|
||||||
|
// name="var22",numchild="1",type="const QString ...} }}}
|
||||||
|
//
|
||||||
|
// In both cases, iterating over the children of stack-args/frame/args
|
||||||
|
// is ok.
|
||||||
|
m_currentFunctionArgs.clear();
|
||||||
|
if (response.resultClass == GdbResultDone) {
|
||||||
|
const GdbMi list = response.data.findChild("stack-args");
|
||||||
|
const GdbMi frame = list.findChild("frame");
|
||||||
|
const GdbMi args = frame.findChild("args");
|
||||||
|
m_currentFunctionArgs = args.children();
|
||||||
|
} else {
|
||||||
|
qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen"
|
||||||
|
<< response.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
// stage 2/2
|
||||||
|
|
||||||
|
// There could be shadowed variables
|
||||||
|
QList<GdbMi> locals = response.data.findChild("locals").children();
|
||||||
|
locals += m_currentFunctionArgs;
|
||||||
|
QMap<QByteArray, int> seen;
|
||||||
|
// If desired, retrieve list of uninitialized variables looking at
|
||||||
|
// the current frame. This is invoked first time after a stop from
|
||||||
|
// handleStop1, which passes on the frame as cookie. The whole stack
|
||||||
|
// is not known at this point.
|
||||||
|
QStringList uninitializedVariables;
|
||||||
|
if (theDebuggerAction(UseCodeModel)->isChecked()) {
|
||||||
|
const StackFrame frame = qVariantCanConvert<Debugger::Internal::StackFrame>(response.cookie) ?
|
||||||
|
qVariantValue<Debugger::Internal::StackFrame>(response.cookie) :
|
||||||
|
m_manager->stackHandler()->currentFrame();
|
||||||
|
if (frame.isUsable())
|
||||||
|
getUninitializedVariables(m_manager->cppCodeModelSnapshot(),
|
||||||
|
frame.function, frame.file, frame.line,
|
||||||
|
&uninitializedVariables);
|
||||||
|
}
|
||||||
|
QList<WatchData> list;
|
||||||
|
foreach (const GdbMi &item, locals) {
|
||||||
|
const WatchData data = localVariable(item, uninitializedVariables, &seen);
|
||||||
|
if (data.isValid())
|
||||||
|
list.push_back(data);
|
||||||
|
}
|
||||||
|
manager()->watchHandler()->insertBulkData(list);
|
||||||
|
manager()->watchHandler()->updateWatchers();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GdbEngine::checkDebuggingHelpersClassic()
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (!manager()->qtDumperLibraryEnabled())
|
||||||
|
return false;
|
||||||
|
const QString lib = qtDumperLibraryName();
|
||||||
|
//qDebug() << "DUMPERLIB:" << lib;
|
||||||
|
const QFileInfo fi(lib);
|
||||||
|
if (!fi.exists()) {
|
||||||
|
const QStringList &locations = manager()->qtDumperLibraryLocations();
|
||||||
|
const QString loc = locations.join(QLatin1String(", "));
|
||||||
|
const QString msg = tr("The debugging helper library was not found at %1.").arg(loc);
|
||||||
|
debugMessage(msg);
|
||||||
|
manager()->showQtDumperLibraryWarning(msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleQueryDebuggingHelperClassic(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
const double dumperVersionRequired = 1.0;
|
||||||
|
//qDebug() << "DATA DUMPER TRIAL:" << response.toString();
|
||||||
|
|
||||||
|
GdbMi contents;
|
||||||
|
QTC_ASSERT(parseConsoleStream(response, &contents), qDebug() << response.toString());
|
||||||
|
const bool ok = m_dumperHelper.parseQuery(contents, QtDumperHelper::GdbDebugger)
|
||||||
|
&& m_dumperHelper.typeCount();
|
||||||
|
if (ok) {
|
||||||
|
// Get version and sizes from dumpers. Expression cache
|
||||||
|
// currently causes errors.
|
||||||
|
const double dumperVersion = getDumperVersion(contents);
|
||||||
|
if (dumperVersion < dumperVersionRequired) {
|
||||||
|
manager()->showQtDumperLibraryWarning(
|
||||||
|
QtDumperHelper::msgDumperOutdated(dumperVersionRequired, dumperVersion));
|
||||||
|
m_debuggingHelperState = DebuggingHelperUnavailable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_debuggingHelperState = DebuggingHelperAvailable;
|
||||||
|
const QString successMsg = tr("Dumper version %1, %n custom dumpers found.",
|
||||||
|
0, m_dumperHelper.typeCount()).arg(dumperVersion);
|
||||||
|
showStatusMessage(successMsg);
|
||||||
|
} else {
|
||||||
|
if (!m_dumperInjectionLoad) // Retry if thread has not terminated yet.
|
||||||
|
m_debuggingHelperState = DebuggingHelperUnavailable;
|
||||||
|
showStatusMessage(tr("Debugging helpers not found."));
|
||||||
|
}
|
||||||
|
//qDebug() << m_dumperHelper.toString(true);
|
||||||
|
//qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
|
||||||
|
}
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Debugger
|
||||||
@@ -19,6 +19,8 @@ HEADERS += \
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/gdbmi.cpp \
|
$$PWD/gdbmi.cpp \
|
||||||
$$PWD/gdbengine.cpp \
|
$$PWD/gdbengine.cpp \
|
||||||
|
$$PWD/classicgdbengine.cpp \
|
||||||
|
$$PWD/pythongdbengine.cpp \
|
||||||
$$PWD/gdboptionspage.cpp \
|
$$PWD/gdboptionspage.cpp \
|
||||||
$$PWD/trkoptions.cpp \
|
$$PWD/trkoptions.cpp \
|
||||||
$$PWD/trkoptionswidget.cpp \
|
$$PWD/trkoptionswidget.cpp \
|
||||||
|
|||||||
@@ -90,11 +90,18 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define DIVERT(func, pars) \
|
||||||
|
do { \
|
||||||
|
if (hasPython()) \
|
||||||
|
func ## Python(pars); \
|
||||||
|
else \
|
||||||
|
func ## Plain(pars); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
//#define DEBUG_PENDING 1
|
//#define DEBUG_PENDING 1
|
||||||
//#define DEBUG_SUBITEM 1
|
|
||||||
|
|
||||||
#if DEBUG_PENDING
|
#if DEBUG_PENDING
|
||||||
# define PENDING_DEBUG(s) qDebug() << s
|
# define PENDING_DEBUG(s) qDebug() << s
|
||||||
@@ -105,7 +112,7 @@ namespace Internal {
|
|||||||
|
|
||||||
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
|
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
|
||||||
|
|
||||||
static QByteArray tooltipINameForExpression(const QByteArray &exp)
|
QByteArray GdbEngine::tooltipINameForExpression(const QByteArray &exp)
|
||||||
{
|
{
|
||||||
// FIXME: 'exp' can contain illegal characters
|
// FIXME: 'exp' can contain illegal characters
|
||||||
//return "tooltip." + exp;
|
//return "tooltip." + exp;
|
||||||
@@ -153,28 +160,6 @@ static bool isAccessSpecifier(const QByteArray &ba)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// reads a MI-encoded item frome the consolestream
|
|
||||||
static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
|
|
||||||
{
|
|
||||||
GdbMi output = response.data.findChild("consolestreamoutput");
|
|
||||||
QByteArray out = output.data();
|
|
||||||
|
|
||||||
int markerPos = out.indexOf('"') + 1; // position of 'success marker'
|
|
||||||
if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f'
|
|
||||||
// custom dumper produced no output
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
out = out.mid(markerPos + 1);
|
|
||||||
out = out.left(out.lastIndexOf('"'));
|
|
||||||
// optimization: dumper output never needs real C unquoting
|
|
||||||
out.replace('\\', "");
|
|
||||||
|
|
||||||
contents->fromStringMultiple(out);
|
|
||||||
//qDebug() << "CONTENTS" << contents->toString(true);
|
|
||||||
return contents->isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
|
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
|
||||||
{
|
{
|
||||||
GdbMi output = response.data.findChild("consolestreamoutput");
|
GdbMi output = response.data.findChild("consolestreamoutput");
|
||||||
@@ -276,7 +261,7 @@ void GdbEngine::initializeVariables()
|
|||||||
m_gdbVersion = 100;
|
m_gdbVersion = 100;
|
||||||
m_gdbBuildVersion = -1;
|
m_gdbBuildVersion = -1;
|
||||||
m_isMacGdb = false;
|
m_isMacGdb = false;
|
||||||
m_isSynchronous = false;
|
m_hasPython = false;
|
||||||
m_registerNamesListed = false;
|
m_registerNamesListed = false;
|
||||||
|
|
||||||
m_fullToShortName.clear();
|
m_fullToShortName.clear();
|
||||||
@@ -698,8 +683,8 @@ void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
|
|||||||
debugMessage(_("FOUND PID %1").arg(pid));
|
debugMessage(_("FOUND PID %1").arg(pid));
|
||||||
|
|
||||||
handleInferiorPidChanged(pid);
|
handleInferiorPidChanged(pid);
|
||||||
if (m_dumperInjectionLoad)
|
if (m_dumperInjectionLoad && !hasPython())
|
||||||
tryLoadDebuggingHelpers();
|
tryLoadDebuggingHelpersClassic();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
|
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
|
||||||
@@ -1023,17 +1008,10 @@ void GdbEngine::executeDebuggerCommand(const QString &command)
|
|||||||
// Called from CoreAdapter and AttachAdapter
|
// Called from CoreAdapter and AttachAdapter
|
||||||
void GdbEngine::updateAll()
|
void GdbEngine::updateAll()
|
||||||
{
|
{
|
||||||
PENDING_DEBUG("UPDATING ALL\n");
|
if (hasPython())
|
||||||
QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
|
updateAllPython();
|
||||||
tryLoadDebuggingHelpers();
|
else
|
||||||
reloadModulesInternal();
|
updateAllClassic();
|
||||||
postCommand("-stack-list-frames", WatchUpdate, CB(handleStackListFrames),
|
|
||||||
QVariant::fromValue<StackCookie>(StackCookie(false, true)));
|
|
||||||
manager()->stackHandler()->setCurrentIndex(0);
|
|
||||||
if (supportsThreads())
|
|
||||||
postCommand("-thread-list-ids", WatchUpdate, CB(handleStackListThreads), 0);
|
|
||||||
manager()->reloadRegisters();
|
|
||||||
updateLocals();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::handleQuerySources(const GdbResponse &response)
|
void GdbEngine::handleQuerySources(const GdbResponse &response)
|
||||||
@@ -1324,7 +1302,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
if (isSynchroneous())
|
if (isSynchroneous())
|
||||||
initHelpers = false;
|
initHelpers = false;
|
||||||
if (initHelpers) {
|
if (initHelpers) {
|
||||||
tryLoadDebuggingHelpers();
|
tryLoadDebuggingHelpersClassic();
|
||||||
QVariant var = QVariant::fromValue<GdbMi>(data);
|
QVariant var = QVariant::fromValue<GdbMi>(data);
|
||||||
postCommand("p 4", CB(handleStop1), var); // dummy
|
postCommand("p 4", CB(handleStop1), var); // dummy
|
||||||
} else {
|
} else {
|
||||||
@@ -1483,13 +1461,13 @@ void GdbEngine::handleShowVersion(const GdbResponse &response)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::handleIsSynchroneous(const GdbResponse &response)
|
void GdbEngine::handleHasPython(const GdbResponse &response)
|
||||||
{
|
{
|
||||||
Q_UNUSED(response);
|
Q_UNUSED(response);
|
||||||
if (response.resultClass == GdbResultDone) {
|
if (response.resultClass == GdbResultDone) {
|
||||||
m_isSynchronous = true;
|
m_hasPython = true;
|
||||||
} else {
|
} else {
|
||||||
m_isSynchronous = false;
|
m_hasPython = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1867,19 +1845,6 @@ void GdbEngine::setTokenBarrier()
|
|||||||
m_oldestAcceptableToken = currentToken();
|
m_oldestAcceptableToken = currentToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::setDebugDebuggingHelpers(const QVariant &on)
|
|
||||||
{
|
|
||||||
if (on.toBool()) {
|
|
||||||
debugMessage(_("SWITCHING ON DUMPER DEBUGGING"));
|
|
||||||
postCommand("set unwindonsignal off");
|
|
||||||
m_manager->breakByFunction(_("qDumpObjectData440"));
|
|
||||||
//updateLocals();
|
|
||||||
} else {
|
|
||||||
debugMessage(_("SWITCHING OFF DUMPER DEBUGGING"));
|
|
||||||
postCommand("set unwindonsignal on");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -2754,8 +2719,8 @@ bool GdbEngine::supportsThreads() const
|
|||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static QString m_toolTipExpression;
|
QString GdbEngine::m_toolTipExpression;
|
||||||
static QPoint m_toolTipPos;
|
QPoint GdbEngine::m_toolTipPos;
|
||||||
|
|
||||||
bool GdbEngine::showToolTip()
|
bool GdbEngine::showToolTip()
|
||||||
{
|
{
|
||||||
@@ -2934,14 +2899,6 @@ static void setWatchDataSAddress(WatchData &data, const GdbMi &mi)
|
|||||||
data.saddr = mi.data();
|
data.saddr = mi.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::setUseDebuggingHelpers(const QVariant &on)
|
|
||||||
{
|
|
||||||
//qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
|
|
||||||
Q_UNUSED(on)
|
|
||||||
setTokenBarrier();
|
|
||||||
updateLocals();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::setAutoDerefPointers(const QVariant &on)
|
void GdbEngine::setAutoDerefPointers(const QVariant &on)
|
||||||
{
|
{
|
||||||
Q_UNUSED(on)
|
Q_UNUSED(on)
|
||||||
@@ -2971,259 +2928,6 @@ bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
|
|||||||
return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
|
return m_dumperHelper.type(type) != QtDumperHelper::UnknownType;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline QString msgRetrievingWatchData(int pending)
|
|
||||||
{
|
|
||||||
return GdbEngine::tr("Retrieving data for watch view (%n requests pending)...", 0, pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::runDirectDebuggingHelper(const WatchData &data, bool dumpChildren)
|
|
||||||
{
|
|
||||||
Q_UNUSED(dumpChildren)
|
|
||||||
QByteArray type = data.type.toLatin1();
|
|
||||||
QByteArray cmd;
|
|
||||||
|
|
||||||
if (type == "QString" || type.endsWith("::QString"))
|
|
||||||
cmd = "qdumpqstring (&(" + data.exp + "))";
|
|
||||||
else if (type == "QStringList" || type.endsWith("::QStringList"))
|
|
||||||
cmd = "qdumpqstringlist (&(" + data.exp + "))";
|
|
||||||
|
|
||||||
QVariant var;
|
|
||||||
var.setValue(data);
|
|
||||||
postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3), var);
|
|
||||||
|
|
||||||
showStatusMessage(msgRetrievingWatchData(m_pendingRequests + 1), 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::runDebuggingHelper(const WatchData &data0, bool dumpChildren)
|
|
||||||
{
|
|
||||||
if (m_debuggingHelperState != DebuggingHelperAvailable) {
|
|
||||||
runDirectDebuggingHelper(data0, dumpChildren);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
WatchData data = data0;
|
|
||||||
|
|
||||||
// Avoid endless loops created by faulty dumpers.
|
|
||||||
QByteArray processedName = QByteArray::number(dumpChildren) + '-' + data.iname;
|
|
||||||
if (m_processedNames.contains(processedName)) {
|
|
||||||
gdbInputAvailable(LogStatus,
|
|
||||||
_("<Breaking endless loop for " + data.iname + ">"));
|
|
||||||
data.setAllUnneeded();
|
|
||||||
data.setValue(_("<unavailable>"));
|
|
||||||
data.setHasChildren(false);
|
|
||||||
insertData(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_processedNames.insert(processedName);
|
|
||||||
|
|
||||||
QByteArray params;
|
|
||||||
QStringList extraArgs;
|
|
||||||
const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
|
|
||||||
m_dumperHelper.evaluationParameters(data, td, QtDumperHelper::GdbDebugger, ¶ms, &extraArgs);
|
|
||||||
|
|
||||||
//int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
|
|
||||||
//int protocol = data.iname.startsWith("watch") ? 3 : 2;
|
|
||||||
const int protocol = 2;
|
|
||||||
//int protocol = isDisplayedIName(data.iname) ? 3 : 2;
|
|
||||||
|
|
||||||
QByteArray addr;
|
|
||||||
if (data.addr.startsWith("0x"))
|
|
||||||
addr = "(void*)" + data.addr;
|
|
||||||
else if (data.exp.isEmpty()) // happens e.g. for QAbstractItem
|
|
||||||
addr = "0";
|
|
||||||
else
|
|
||||||
addr = "&(" + data.exp + ')';
|
|
||||||
|
|
||||||
sendWatchParameters(params);
|
|
||||||
|
|
||||||
QString cmd;
|
|
||||||
QTextStream(&cmd) << "call " << "(void*)qDumpObjectData440(" <<
|
|
||||||
protocol << ",0," << addr << ',' << (dumpChildren ? "1" : "0")
|
|
||||||
<< ',' << extraArgs.join(QString(_c(','))) << ')';
|
|
||||||
|
|
||||||
postCommand(cmd.toLatin1(), WatchUpdate | NonCriticalResponse);
|
|
||||||
|
|
||||||
showStatusMessage(msgRetrievingWatchData(m_pendingRequests + 1), 10000);
|
|
||||||
|
|
||||||
// retrieve response
|
|
||||||
postCommand("p (char*)&qDumpOutBuffer", WatchUpdate,
|
|
||||||
CB(handleDebuggingHelperValue2), qVariantFromValue(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::createGdbVariable(const WatchData &data)
|
|
||||||
{
|
|
||||||
if (data.iname == "local.flist.0") {
|
|
||||||
int i = 1;
|
|
||||||
Q_UNUSED(i);
|
|
||||||
}
|
|
||||||
postCommand("-var-delete \"" + data.iname + '"', WatchUpdate);
|
|
||||||
QByteArray exp = data.exp;
|
|
||||||
if (exp.isEmpty() && data.addr.startsWith("0x"))
|
|
||||||
exp = "*(" + gdbQuoteTypes(data.type).toLatin1() + "*)" + data.addr;
|
|
||||||
QVariant val = QVariant::fromValue<WatchData>(data);
|
|
||||||
postCommand("-var-create \"" + data.iname + "\" * \"" + exp + '"',
|
|
||||||
WatchUpdate, CB(handleVarCreate), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::updateSubItem(const WatchData &data0)
|
|
||||||
{
|
|
||||||
WatchData data = data0;
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM:" << data.toString();
|
|
||||||
#endif
|
|
||||||
QTC_ASSERT(data.isValid(), return);
|
|
||||||
|
|
||||||
// in any case we need the type first
|
|
||||||
if (data.isTypeNeeded()) {
|
|
||||||
// This should only happen if we don't have a variable yet.
|
|
||||||
// Let's play safe, though.
|
|
||||||
if (!data.variable.isEmpty()) {
|
|
||||||
// Update: It does so for out-of-scope watchers.
|
|
||||||
#if 1
|
|
||||||
qDebug() << "FIXME: GdbEngine::updateSubItem:"
|
|
||||||
<< data.toString() << "should not happen";
|
|
||||||
#else
|
|
||||||
data.setType(WatchData::msgNotInScope());
|
|
||||||
data.setValue(WatchData::msgNotInScope());
|
|
||||||
data.setHasChildren(false);
|
|
||||||
insertData(data);
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
// The WatchVarCreate handler will receive type information
|
|
||||||
// and re-insert a WatchData item with correct type, so
|
|
||||||
// we will not re-enter this bit.
|
|
||||||
// FIXME: Concurrency issues?
|
|
||||||
createGdbVariable(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should have a type now. this is relied upon further below
|
|
||||||
QTC_ASSERT(!data.type.isEmpty(), return);
|
|
||||||
|
|
||||||
// a common case that can be easily solved
|
|
||||||
if (data.isChildrenNeeded() && isPointerType(data.type)
|
|
||||||
&& !hasDebuggingHelperForType(data.type)) {
|
|
||||||
// We sometimes know what kind of children pointers have
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "IT'S A POINTER";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (theDebuggerBoolSetting(AutoDerefPointers)) {
|
|
||||||
// Try automatic dereferentiation
|
|
||||||
data.exp = "(*(" + data.exp + "))";
|
|
||||||
data.type = data.type + _("."); // FIXME: fragile HACK to avoid recursion
|
|
||||||
insertData(data);
|
|
||||||
} else {
|
|
||||||
data.setChildrenUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
WatchData data1;
|
|
||||||
data1.iname = data.iname + ".*";
|
|
||||||
data1.name = QLatin1Char('*') + data.name;
|
|
||||||
data1.exp = "(*(" + data.exp + "))";
|
|
||||||
data1.type = stripPointerType(data.type);
|
|
||||||
data1.setValueNeeded();
|
|
||||||
data1.setChildrenUnneeded();
|
|
||||||
insertData(data1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isValueNeeded() && hasDebuggingHelperForType(data.type)) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
|
|
||||||
#endif
|
|
||||||
runDebuggingHelper(data, manager()->watchHandler()->isExpandedIName(data.iname));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (data.isValueNeeded() && data.exp.isEmpty()) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
|
|
||||||
#endif
|
|
||||||
data.setError("<no expression given>");
|
|
||||||
insertData(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (data.isValueNeeded() && data.variable.isEmpty()) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
|
|
||||||
#endif
|
|
||||||
createGdbVariable(data);
|
|
||||||
// the WatchVarCreate handler will re-insert a WatchData
|
|
||||||
// item, with valueNeeded() set.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isValueNeeded()) {
|
|
||||||
QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: VALUE";
|
|
||||||
#endif
|
|
||||||
QByteArray cmd = "-var-evaluate-expression \"" + data.iname + '"';
|
|
||||||
postCommand(cmd, WatchUpdate, CB(handleEvaluateExpression),
|
|
||||||
QVariant::fromValue(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
|
|
||||||
#endif
|
|
||||||
runDebuggingHelper(data, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isChildrenNeeded() && data.variable.isEmpty()) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
|
|
||||||
#endif
|
|
||||||
createGdbVariable(data);
|
|
||||||
// the WatchVarCreate handler will re-insert a WatchData
|
|
||||||
// item, with childrenNeeded() set.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isChildrenNeeded()) {
|
|
||||||
QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
|
|
||||||
QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
|
|
||||||
postCommand(cmd, WatchUpdate, CB(handleVarListChildren), QVariant::fromValue(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isHasChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
|
|
||||||
#endif
|
|
||||||
runDebuggingHelper(data, manager()->watchHandler()->isExpandedIName(data.iname));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#if !X
|
|
||||||
if (data.isHasChildrenNeeded() && data.variable.isEmpty()) {
|
|
||||||
#if DEBUG_SUBITEM
|
|
||||||
qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
|
|
||||||
#endif
|
|
||||||
createGdbVariable(data);
|
|
||||||
// the WatchVarCreate handler will re-insert a WatchData
|
|
||||||
// item, with childrenNeeded() set.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
if (data.isHasChildrenNeeded()) {
|
|
||||||
QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
|
|
||||||
QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
|
|
||||||
postCommand(cmd, Discardable,
|
|
||||||
CB(handleVarListChildren), QVariant::fromValue(data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "FIXME: UPDATE SUBITEM:" << data.toString();
|
|
||||||
QTC_ASSERT(false, return);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::updateWatchData(const WatchData &data)
|
void GdbEngine::updateWatchData(const WatchData &data)
|
||||||
{
|
{
|
||||||
@@ -3281,7 +2985,7 @@ void GdbEngine::updateWatchDataHelper(const WatchData &data)
|
|||||||
//qDebug() << data.toString();
|
//qDebug() << data.toString();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
updateSubItem(data);
|
updateSubItemClassic(data);
|
||||||
//PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
|
//PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
|
||||||
--m_pendingRequests;
|
--m_pendingRequests;
|
||||||
PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingRequests);
|
PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingRequests);
|
||||||
@@ -3302,51 +3006,7 @@ void GdbEngine::rebuildModel()
|
|||||||
showToolTip();
|
showToolTip();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline double getDumperVersion(const GdbMi &contents)
|
static QByteArray arrayFillCommand(const char *array, const QByteArray ¶ms)
|
||||||
{
|
|
||||||
const GdbMi dumperVersionG = contents.findChild("dumperversion");
|
|
||||||
if (dumperVersionG.type() != GdbMi::Invalid) {
|
|
||||||
bool ok;
|
|
||||||
const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
|
|
||||||
if (ok)
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleQueryDebuggingHelper(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
const double dumperVersionRequired = 1.0;
|
|
||||||
//qDebug() << "DATA DUMPER TRIAL:" << response.toString();
|
|
||||||
|
|
||||||
GdbMi contents;
|
|
||||||
QTC_ASSERT(parseConsoleStream(response, &contents), qDebug() << response.toString());
|
|
||||||
const bool ok = m_dumperHelper.parseQuery(contents, QtDumperHelper::GdbDebugger)
|
|
||||||
&& m_dumperHelper.typeCount();
|
|
||||||
if (ok) {
|
|
||||||
// Get version and sizes from dumpers. Expression cache
|
|
||||||
// currently causes errors.
|
|
||||||
const double dumperVersion = getDumperVersion(contents);
|
|
||||||
if (dumperVersion < dumperVersionRequired) {
|
|
||||||
manager()->showQtDumperLibraryWarning(
|
|
||||||
QtDumperHelper::msgDumperOutdated(dumperVersionRequired, dumperVersion));
|
|
||||||
m_debuggingHelperState = DebuggingHelperUnavailable;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_debuggingHelperState = DebuggingHelperAvailable;
|
|
||||||
const QString successMsg = tr("Dumper version %1, %n custom dumpers found.",
|
|
||||||
0, m_dumperHelper.typeCount()).arg(dumperVersion);
|
|
||||||
showStatusMessage(successMsg);
|
|
||||||
} else {
|
|
||||||
if (!m_dumperInjectionLoad) // Retry if thread has not terminated yet.
|
|
||||||
m_debuggingHelperState = DebuggingHelperUnavailable;
|
|
||||||
showStatusMessage(tr("Debugging helpers not found."));
|
|
||||||
}
|
|
||||||
//qDebug() << m_dumperHelper.toString(true);
|
|
||||||
//qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QByteArray arrayFillCommand(const char *array, const QByteArray ¶ms)
|
|
||||||
{
|
{
|
||||||
char buf[50];
|
char buf[50];
|
||||||
sprintf(buf, "set {char[%d]} &%s = {", params.size(), array);
|
sprintf(buf, "set {char[%d]} &%s = {", params.size(), array);
|
||||||
@@ -3465,45 +3125,6 @@ void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::handleDebuggingHelperValue2(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
WatchData data = response.cookie.value<WatchData>();
|
|
||||||
QTC_ASSERT(data.isValid(), return);
|
|
||||||
|
|
||||||
// The real dumper might have aborted without giving any answers.
|
|
||||||
// Remove traces of the question, too.
|
|
||||||
if (m_cookieForToken.contains(response.token - 1)) {
|
|
||||||
m_cookieForToken.remove(response.token - 1);
|
|
||||||
debugMessage(_("DETECTING LOST COMMAND %1").arg(response.token - 1));
|
|
||||||
--m_pendingRequests;
|
|
||||||
data.setError(WatchData::msgNotInScope());
|
|
||||||
insertData(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//qDebug() << "CUSTOM VALUE RESULT:" << response.toString();
|
|
||||||
//qDebug() << "FOR DATA:" << data.toString() << response.resultClass;
|
|
||||||
if (response.resultClass != GdbResultDone) {
|
|
||||||
qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA:" << data.toString();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GdbMi contents;
|
|
||||||
if (!parseConsoleStream(response, &contents)) {
|
|
||||||
data.setError(WatchData::msgNotInScope());
|
|
||||||
insertData(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setWatchDataType(data, response.data.findChild("type"));
|
|
||||||
setWatchDataDisplayedType(data, response.data.findChild("displaytype"));
|
|
||||||
QList<WatchData> list;
|
|
||||||
handleChildren(data, contents, &list);
|
|
||||||
//for (int i = 0; i != list.size(); ++i)
|
|
||||||
// qDebug() << "READ: " << list.at(i).toString();
|
|
||||||
manager()->watchHandler()->insertBulkData(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
|
void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
|
||||||
QList<WatchData> *list)
|
QList<WatchData> *list)
|
||||||
{
|
{
|
||||||
@@ -3575,280 +3196,15 @@ void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::handleDebuggingHelperValue3(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
if (response.resultClass == GdbResultDone) {
|
|
||||||
WatchData data = response.cookie.value<WatchData>();
|
|
||||||
QByteArray out = response.data.findChild("consolestreamoutput").data();
|
|
||||||
while (out.endsWith(' ') || out.endsWith('\n'))
|
|
||||||
out.chop(1);
|
|
||||||
QList<QByteArray> list = out.split(' ');
|
|
||||||
//qDebug() << "RECEIVED" << response.toString() << "FOR" << data0.toString()
|
|
||||||
// << " STREAM:" << out;
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
//: Value for variable
|
|
||||||
data.setError(WatchData::msgNotInScope());
|
|
||||||
data.setAllUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
} else if (data.type == __("QString")
|
|
||||||
|| data.type.endsWith(__("::QString"))) {
|
|
||||||
QList<QByteArray> list = out.split(' ');
|
|
||||||
QString str;
|
|
||||||
int l = out.isEmpty() ? 0 : list.size();
|
|
||||||
for (int i = 0; i < l; ++i)
|
|
||||||
str.append(list.at(i).toInt());
|
|
||||||
data.setValue(_c('"') + str + _c('"'));
|
|
||||||
data.setHasChildren(false);
|
|
||||||
data.setAllUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
} else if (data.type == __("QStringList")
|
|
||||||
|| data.type.endsWith(__("::QStringList"))) {
|
|
||||||
if (out.isEmpty()) {
|
|
||||||
data.setValue(tr("<0 items>"));
|
|
||||||
data.setHasChildren(false);
|
|
||||||
data.setAllUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
} else {
|
|
||||||
int l = list.size();
|
|
||||||
//: In string list
|
|
||||||
data.setValue(tr("<%n items>", 0, l));
|
|
||||||
data.setHasChildren(!list.empty());
|
|
||||||
data.setAllUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
for (int i = 0; i < l; ++i) {
|
|
||||||
WatchData data1;
|
|
||||||
data1.name = _("[%1]").arg(i);
|
|
||||||
data1.type = data.type.left(data.type.size() - 4);
|
|
||||||
data1.iname = data.iname + '.' + QByteArray::number(i);
|
|
||||||
data1.addr = list.at(i);
|
|
||||||
data1.exp = "((" + gdbQuoteTypes(data1.type).toLatin1() + "*)" + data1.addr + ")";
|
|
||||||
data1.setHasChildren(false);
|
|
||||||
data1.setValueNeeded();
|
|
||||||
QByteArray cmd = "qdumpqstring (" + data1.exp + ')';
|
|
||||||
QVariant var;
|
|
||||||
var.setValue(data1);
|
|
||||||
postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3), var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//: Value for variable
|
|
||||||
data.setError(WatchData::msgNotInScope());
|
|
||||||
data.setAllUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
WatchData data = response.cookie.value<WatchData>();
|
|
||||||
data.setError(WatchData::msgNotInScope());
|
|
||||||
data.setAllUnneeded();
|
|
||||||
insertData(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::updateLocals(const QVariant &cookie)
|
void GdbEngine::updateLocals(const QVariant &cookie)
|
||||||
{
|
{
|
||||||
m_pendingRequests = 0;
|
m_pendingRequests = 0;
|
||||||
if (isSynchroneous()) {
|
if (hasPython())
|
||||||
if (m_gdbAdapter->isTrkAdapter()) {
|
updateLocalsPython(QByteArray());
|
||||||
postCommand("-stack-list-locals 0",
|
|
||||||
WatchUpdate, CB(handleStackListLocals0));
|
|
||||||
} else {
|
|
||||||
updateLocalsSync(QByteArray());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_processedNames.clear();
|
|
||||||
|
|
||||||
PENDING_DEBUG("\nRESET PENDING");
|
|
||||||
//m_toolTipCache.clear();
|
|
||||||
m_toolTipExpression.clear();
|
|
||||||
manager()->watchHandler()->beginCycle();
|
|
||||||
|
|
||||||
// Asynchronous load of injected library, initialize in first stop
|
|
||||||
if (m_dumperInjectionLoad && m_debuggingHelperState == DebuggingHelperLoadTried
|
|
||||||
&& m_dumperHelper.typeCount() == 0
|
|
||||||
&& inferiorPid() > 0)
|
|
||||||
tryQueryDebuggingHelpers();
|
|
||||||
|
|
||||||
QByteArray level = QByteArray::number(currentFrame());
|
|
||||||
// '2' is 'list with type and value'
|
|
||||||
QByteArray cmd = "-stack-list-arguments 2 " + level + ' ' + level;
|
|
||||||
postCommand(cmd, WatchUpdate, CB(handleStackListArguments));
|
|
||||||
// '2' is 'list with type and value'
|
|
||||||
postCommand("-stack-list-locals 2", WatchUpdate,
|
|
||||||
CB(handleStackListLocals), cookie); // stage 2/2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleStackListLocals0(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
if (response.resultClass == GdbResultDone) {
|
|
||||||
// 44^done,data={locals=[name="model",name="backString",...]}
|
|
||||||
QByteArray varList = "vars"; // Dummy entry, will be stripped by dumper.
|
|
||||||
foreach (const GdbMi &child, response.data.findChild("locals").children()) {
|
|
||||||
varList.append(',');
|
|
||||||
varList.append(child.data());
|
|
||||||
}
|
|
||||||
updateLocalsSync(varList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::updateLocalsSync(const QByteArray &varList)
|
|
||||||
{
|
|
||||||
m_processedNames.clear();
|
|
||||||
manager()->watchHandler()->beginCycle();
|
|
||||||
//m_toolTipExpression.clear();
|
|
||||||
WatchHandler *handler = m_manager->watchHandler();
|
|
||||||
|
|
||||||
QByteArray expanded;
|
|
||||||
QSet<QByteArray> expandedINames = handler->expandedINames();
|
|
||||||
QSetIterator<QByteArray> jt(expandedINames);
|
|
||||||
while (jt.hasNext()) {
|
|
||||||
expanded.append(jt.next());
|
|
||||||
expanded.append(',');
|
|
||||||
}
|
|
||||||
if (expanded.isEmpty())
|
|
||||||
expanded.append("defaults,");
|
|
||||||
expanded.chop(1);
|
|
||||||
|
|
||||||
QByteArray watchers;
|
|
||||||
if (!m_toolTipExpression.isEmpty())
|
|
||||||
watchers += m_toolTipExpression.toLatin1()
|
|
||||||
+ "#" + tooltipINameForExpression(m_toolTipExpression.toLatin1());
|
|
||||||
|
|
||||||
QHash<QByteArray, int> watcherNames = handler->watcherNames();
|
|
||||||
QHashIterator<QByteArray, int> it(watcherNames);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next();
|
|
||||||
if (!watchers.isEmpty())
|
|
||||||
watchers += "##";
|
|
||||||
if (it.key() == WatchHandler::watcherEditPlaceHolder().toLatin1())
|
|
||||||
watchers += "<Edit>#watch." + QByteArray::number(it.value());
|
|
||||||
else
|
else
|
||||||
watchers += it.key() + "#watch." + QByteArray::number(it.value());
|
updateLocalsClassic(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray options;
|
|
||||||
if (theDebuggerBoolSetting(UseDebuggingHelpers))
|
|
||||||
options += "fancy,";
|
|
||||||
if (theDebuggerBoolSetting(AutoDerefPointers))
|
|
||||||
options += "autoderef,";
|
|
||||||
if (options.isEmpty())
|
|
||||||
options += "defaults,";
|
|
||||||
options.chop(1);
|
|
||||||
|
|
||||||
postCommand("bb " + options + " @" + varList + ' '
|
|
||||||
+ expanded + ' ' + watchers.toHex(),
|
|
||||||
WatchUpdate, CB(handleStackFrame));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GdbEngine::handleStackFrame(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
if (response.resultClass == GdbResultDone) {
|
|
||||||
QByteArray out = response.data.findChild("consolestreamoutput").data();
|
|
||||||
while (out.endsWith(' ') || out.endsWith('\n'))
|
|
||||||
out.chop(1);
|
|
||||||
//qDebug() << "SECOND CHUNK: " << out;
|
|
||||||
int pos = out.indexOf("data=");
|
|
||||||
if (pos != 0) {
|
|
||||||
qDebug() << "DISCARDING JUNK AT BEGIN OF RESPONSE: "
|
|
||||||
<< out.left(pos);
|
|
||||||
out = out.mid(pos);
|
|
||||||
}
|
|
||||||
GdbMi all;
|
|
||||||
all.fromStringMultiple(out);
|
|
||||||
//qDebug() << "ALL: " << all.toString();
|
|
||||||
|
|
||||||
GdbMi data = all.findChild("data");
|
|
||||||
QList<WatchData> list;
|
|
||||||
foreach (const GdbMi &child, data.children()) {
|
|
||||||
WatchData dummy;
|
|
||||||
dummy.iname = child.findChild("iname").data();
|
|
||||||
dummy.name = _(child.findChild("name").data());
|
|
||||||
//qDebug() << "CHILD: " << child.toString();
|
|
||||||
handleChildren(dummy, child, &list);
|
|
||||||
}
|
|
||||||
manager()->watchHandler()->insertBulkData(list);
|
|
||||||
//for (int i = 0; i != list.size(); ++i)
|
|
||||||
// qDebug() << "LOCAL: " << list.at(i).toString();
|
|
||||||
|
|
||||||
PENDING_DEBUG("AFTER handleStackFrame()");
|
|
||||||
// FIXME: This should only be used when updateLocals() was
|
|
||||||
// triggered by expanding an item in the view.
|
|
||||||
if (m_pendingRequests <= 0) {
|
|
||||||
PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n");
|
|
||||||
rebuildModel();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debugMessage(_("DUMPER FAILED: " + response.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleStackListArguments(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
// stage 1/2
|
|
||||||
|
|
||||||
// Linux:
|
|
||||||
// 12^done,stack-args=
|
|
||||||
// [frame={level="0",args=[
|
|
||||||
// {name="argc",type="int",value="1"},
|
|
||||||
// {name="argv",type="char **",value="(char **) 0x7..."}]}]
|
|
||||||
// Mac:
|
|
||||||
// 78^done,stack-args=
|
|
||||||
// {frame={level="0",args={
|
|
||||||
// varobj=
|
|
||||||
// {exp="this",value="0x38a2fab0",name="var21",numchild="3",
|
|
||||||
// type="CurrentDocumentFind * const",typecode="PTR",
|
|
||||||
// dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
|
|
||||||
// block_end_addr="0x3938eb2d"},
|
|
||||||
// varobj=
|
|
||||||
// {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
|
|
||||||
// name="var22",numchild="1",type="const QString ...} }}}
|
|
||||||
//
|
|
||||||
// In both cases, iterating over the children of stack-args/frame/args
|
|
||||||
// is ok.
|
|
||||||
m_currentFunctionArgs.clear();
|
|
||||||
if (response.resultClass == GdbResultDone) {
|
|
||||||
const GdbMi list = response.data.findChild("stack-args");
|
|
||||||
const GdbMi frame = list.findChild("frame");
|
|
||||||
const GdbMi args = frame.findChild("args");
|
|
||||||
m_currentFunctionArgs = args.children();
|
|
||||||
} else {
|
|
||||||
qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen"
|
|
||||||
<< response.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleStackListLocals(const GdbResponse &response)
|
|
||||||
{
|
|
||||||
// stage 2/2
|
|
||||||
|
|
||||||
// There could be shadowed variables
|
|
||||||
QList<GdbMi> locals = response.data.findChild("locals").children();
|
|
||||||
locals += m_currentFunctionArgs;
|
|
||||||
QMap<QByteArray, int> seen;
|
|
||||||
// If desired, retrieve list of uninitialized variables looking at
|
|
||||||
// the current frame. This is invoked first time after a stop from
|
|
||||||
// handleStop1, which passes on the frame as cookie. The whole stack
|
|
||||||
// is not known at this point.
|
|
||||||
QStringList uninitializedVariables;
|
|
||||||
if (theDebuggerAction(UseCodeModel)->isChecked()) {
|
|
||||||
const StackFrame frame = qVariantCanConvert<Debugger::Internal::StackFrame>(response.cookie) ?
|
|
||||||
qVariantValue<Debugger::Internal::StackFrame>(response.cookie) :
|
|
||||||
m_manager->stackHandler()->currentFrame();
|
|
||||||
if (frame.isUsable())
|
|
||||||
getUninitializedVariables(m_manager->cppCodeModelSnapshot(),
|
|
||||||
frame.function, frame.file, frame.line,
|
|
||||||
&uninitializedVariables);
|
|
||||||
}
|
|
||||||
QList<WatchData> list;
|
|
||||||
foreach (const GdbMi &item, locals) {
|
|
||||||
const WatchData data = localVariable(item, uninitializedVariables, &seen);
|
|
||||||
if (data.isValid())
|
|
||||||
list.push_back(data);
|
|
||||||
}
|
|
||||||
manager()->watchHandler()->insertBulkData(list);
|
|
||||||
manager()->watchHandler()->updateWatchers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse a local variable from GdbMi
|
// Parse a local variable from GdbMi
|
||||||
WatchData GdbEngine::localVariable(const GdbMi &item,
|
WatchData GdbEngine::localVariable(const GdbMi &item,
|
||||||
@@ -4104,134 +3460,6 @@ QString GdbEngine::qtDumperLibraryName() const
|
|||||||
return m_manager->qtDumperLibraryName();
|
return m_manager->qtDumperLibraryName();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GdbEngine::checkDebuggingHelpers()
|
|
||||||
{
|
|
||||||
if (!manager()->qtDumperLibraryEnabled())
|
|
||||||
return false;
|
|
||||||
const QString lib = qtDumperLibraryName();
|
|
||||||
//qDebug() << "DUMPERLIB:" << lib;
|
|
||||||
const QFileInfo fi(lib);
|
|
||||||
if (!fi.exists()) {
|
|
||||||
const QStringList &locations = manager()->qtDumperLibraryLocations();
|
|
||||||
const QString loc = locations.join(QLatin1String(", "));
|
|
||||||
const QString msg = tr("The debugging helper library was not found at %1.").arg(loc);
|
|
||||||
debugMessage(msg);
|
|
||||||
manager()->showQtDumperLibraryWarning(msg);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::setDebuggingHelperState(DebuggingHelperState s)
|
|
||||||
{
|
|
||||||
m_debuggingHelperState = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::tryLoadDebuggingHelpers()
|
|
||||||
{
|
|
||||||
if (isSynchroneous())
|
|
||||||
return;
|
|
||||||
switch (m_debuggingHelperState) {
|
|
||||||
case DebuggingHelperUninitialized:
|
|
||||||
break;
|
|
||||||
case DebuggingHelperLoadTried:
|
|
||||||
tryQueryDebuggingHelpers();
|
|
||||||
return;
|
|
||||||
case DebuggingHelperAvailable:
|
|
||||||
case DebuggingHelperUnavailable:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
|
|
||||||
// Load at least gdb macro based dumpers.
|
|
||||||
QFile file(_(":/gdb/gdbmacros.txt"));
|
|
||||||
file.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray contents = file.readAll();
|
|
||||||
m_debuggingHelperState = DebuggingHelperLoadTried;
|
|
||||||
postCommand(contents);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_dumperInjectionLoad && inferiorPid() <= 0) // Need PID to inject
|
|
||||||
return;
|
|
||||||
|
|
||||||
PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
|
|
||||||
m_debuggingHelperState = DebuggingHelperUnavailable;
|
|
||||||
if (!checkDebuggingHelpers())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_debuggingHelperState = DebuggingHelperLoadTried;
|
|
||||||
QByteArray dlopenLib;
|
|
||||||
if (startParameters().startMode == StartRemote)
|
|
||||||
dlopenLib = startParameters().remoteDumperLib.toLocal8Bit();
|
|
||||||
else
|
|
||||||
dlopenLib = manager()->qtDumperLibraryName().toLocal8Bit();
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
if (m_dumperInjectionLoad) {
|
|
||||||
/// Launch asynchronous remote thread to load.
|
|
||||||
SharedLibraryInjector injector(inferiorPid());
|
|
||||||
QString errorMessage;
|
|
||||||
const QString dlopenLibString = _(dlopenLib);
|
|
||||||
if (injector.remoteInject(dlopenLibString, false, &errorMessage)) {
|
|
||||||
debugMessage(_("Dumper injection loading triggered (%1)...").
|
|
||||||
arg(dlopenLibString));
|
|
||||||
} else {
|
|
||||||
debugMessage(_("Dumper loading (%1) failed: %2").
|
|
||||||
arg(dlopenLibString, errorMessage));
|
|
||||||
debugMessage(errorMessage);
|
|
||||||
manager()->showQtDumperLibraryWarning(errorMessage);
|
|
||||||
m_debuggingHelperState = DebuggingHelperUnavailable;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debugMessage(_("Loading dumpers via debugger call (%1)...").
|
|
||||||
arg(_(dlopenLib)));
|
|
||||||
postCommand("sharedlibrary .*"); // for LoadLibraryA
|
|
||||||
//postCommand("handle SIGSEGV pass stop print");
|
|
||||||
//postCommand("set unwindonsignal off");
|
|
||||||
postCommand("call LoadLibraryA(\"" + GdbMi::escapeCString(dlopenLib) + "\")",
|
|
||||||
CB(handleDebuggingHelperSetup));
|
|
||||||
postCommand("sharedlibrary " + dotEscape(dlopenLib));
|
|
||||||
}
|
|
||||||
#elif defined(Q_OS_MAC)
|
|
||||||
//postCommand("sharedlibrary libc"); // for malloc
|
|
||||||
//postCommand("sharedlibrary libdl"); // for dlopen
|
|
||||||
postCommand("call (void)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
|
|
||||||
+ "\", " STRINGIFY(RTLD_NOW) ")",
|
|
||||||
CB(handleDebuggingHelperSetup));
|
|
||||||
//postCommand("sharedlibrary " + dotEscape(dlopenLib));
|
|
||||||
#else
|
|
||||||
//postCommand("p dlopen");
|
|
||||||
postCommand("sharedlibrary libc"); // for malloc
|
|
||||||
postCommand("sharedlibrary libdl"); // for dlopen
|
|
||||||
postCommand("call (void*)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
|
|
||||||
+ "\", " STRINGIFY(RTLD_NOW) ")",
|
|
||||||
CB(handleDebuggingHelperSetup));
|
|
||||||
// some older systems like CentOS 4.6 prefer this:
|
|
||||||
postCommand("call (void*)__dlopen(\"" + GdbMi::escapeCString(dlopenLib)
|
|
||||||
+ "\", " STRINGIFY(RTLD_NOW) ")",
|
|
||||||
CB(handleDebuggingHelperSetup));
|
|
||||||
postCommand("sharedlibrary " + dotEscape(dlopenLib));
|
|
||||||
#endif
|
|
||||||
if (!m_dumperInjectionLoad)
|
|
||||||
tryQueryDebuggingHelpers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::tryQueryDebuggingHelpers()
|
|
||||||
{
|
|
||||||
// retrieve list of dumpable classes
|
|
||||||
postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
|
|
||||||
postCommand("p (char*)&qDumpOutBuffer", CB(handleQueryDebuggingHelper));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::recheckDebuggingHelperAvailability()
|
|
||||||
{
|
|
||||||
if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
|
|
||||||
// retrieve list of dumpable classes
|
|
||||||
postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
|
|
||||||
postCommand("p (char*)&qDumpOutBuffer", CB(handleQueryDebuggingHelper));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::watchPoint(const QPoint &pnt)
|
void GdbEngine::watchPoint(const QPoint &pnt)
|
||||||
{
|
{
|
||||||
//qDebug() << "WATCH " << pnt;
|
//qDebug() << "WATCH " << pnt;
|
||||||
@@ -4530,7 +3758,7 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr
|
|||||||
NonCriticalResponse);
|
NonCriticalResponse);
|
||||||
|
|
||||||
postCommand("-interpreter-exec console \"help bb\"",
|
postCommand("-interpreter-exec console \"help bb\"",
|
||||||
CB(handleIsSynchroneous));
|
CB(handleHasPython));
|
||||||
//postCommand("-enable-timings");
|
//postCommand("-enable-timings");
|
||||||
postCommand("set print static-members off"); // Seemingly doesn't work.
|
postCommand("set print static-members off"); // Seemingly doesn't work.
|
||||||
//postCommand("set debug infrun 1");
|
//postCommand("set debug infrun 1");
|
||||||
@@ -4622,6 +3850,11 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GdbEngine::checkDebuggingHelpers()
|
||||||
|
{
|
||||||
|
return !hasPython() && checkDebuggingHelpersClassic();
|
||||||
|
}
|
||||||
|
|
||||||
void GdbEngine::handleGdbError(QProcess::ProcessError error)
|
void GdbEngine::handleGdbError(QProcess::ProcessError error)
|
||||||
{
|
{
|
||||||
debugMessage(_("HANDLE GDB ERROR"));
|
debugMessage(_("HANDLE GDB ERROR"));
|
||||||
@@ -4758,9 +3991,17 @@ QMessageBox * GdbEngine::showMessageBox(int icon, const QString &title,
|
|||||||
return m_manager->showMessageBox(icon, title, text, buttons);
|
return m_manager->showMessageBox(icon, title, text, buttons);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GdbEngine::isSynchroneous() const
|
void GdbEngine::setUseDebuggingHelpers(const QVariant &on)
|
||||||
{
|
{
|
||||||
return m_isSynchronous;
|
//qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
|
||||||
|
Q_UNUSED(on)
|
||||||
|
setTokenBarrier();
|
||||||
|
updateLocals();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GdbEngine::hasPython() const
|
||||||
|
{
|
||||||
|
return m_hasPython;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -264,17 +264,17 @@ private: ////////// Gdb Output, State & Capability Handling //////////
|
|||||||
void handleStop1(const GdbMi &data);
|
void handleStop1(const GdbMi &data);
|
||||||
StackFrame parseStackFrame(const GdbMi &mi, int level);
|
StackFrame parseStackFrame(const GdbMi &mi, int level);
|
||||||
|
|
||||||
virtual bool isSynchroneous() const;
|
virtual bool hasPython() const;
|
||||||
bool supportsThreads() const;
|
bool supportsThreads() const;
|
||||||
|
|
||||||
// Gdb initialization sequence
|
// Gdb initialization sequence
|
||||||
void handleShowVersion(const GdbResponse &response);
|
void handleShowVersion(const GdbResponse &response);
|
||||||
void handleIsSynchroneous(const GdbResponse &response);
|
void handleHasPython(const GdbResponse &response);
|
||||||
|
|
||||||
int m_gdbVersion; // 6.8.0 is 60800
|
int m_gdbVersion; // 6.8.0 is 60800
|
||||||
int m_gdbBuildVersion; // MAC only?
|
int m_gdbBuildVersion; // MAC only?
|
||||||
bool m_isMacGdb;
|
bool m_isMacGdb;
|
||||||
bool m_isSynchronous; // Can act synchronously?
|
bool m_hasPython;
|
||||||
|
|
||||||
private: ////////// Inferior Management //////////
|
private: ////////// Inferior Management //////////
|
||||||
|
|
||||||
@@ -393,10 +393,11 @@ private: ////////// View & Data Stuff //////////
|
|||||||
// Stack specific stuff
|
// Stack specific stuff
|
||||||
//
|
//
|
||||||
void updateAll();
|
void updateAll();
|
||||||
|
void updateAllClassic();
|
||||||
|
void updateAllPython();
|
||||||
void handleStackListFrames(const GdbResponse &response);
|
void handleStackListFrames(const GdbResponse &response);
|
||||||
void handleStackSelectThread(const GdbResponse &response);
|
void handleStackSelectThread(const GdbResponse &response);
|
||||||
void handleStackListThreads(const GdbResponse &response);
|
void handleStackListThreads(const GdbResponse &response);
|
||||||
void handleStackFrame(const GdbResponse &response);
|
|
||||||
Q_SLOT void reloadStack(bool forceGotoLocation);
|
Q_SLOT void reloadStack(bool forceGotoLocation);
|
||||||
Q_SLOT virtual void reloadFullStack();
|
Q_SLOT virtual void reloadFullStack();
|
||||||
int currentFrame() const;
|
int currentFrame() const;
|
||||||
@@ -418,7 +419,7 @@ private: ////////// View & Data Stuff //////////
|
|||||||
void handleWatchPoint(const GdbResponse &response);
|
void handleWatchPoint(const GdbResponse &response);
|
||||||
|
|
||||||
// FIXME: BaseClass. called to improve situation for a watch item
|
// FIXME: BaseClass. called to improve situation for a watch item
|
||||||
void updateSubItem(const WatchData &data);
|
void updateSubItemClassic(const WatchData &data);
|
||||||
void handleChildren(const WatchData &parent, const GdbMi &child,
|
void handleChildren(const WatchData &parent, const GdbMi &child,
|
||||||
QList<WatchData> *insertions);
|
QList<WatchData> *insertions);
|
||||||
|
|
||||||
@@ -429,10 +430,10 @@ private: ////////// View & Data Stuff //////////
|
|||||||
|
|
||||||
void insertData(const WatchData &data);
|
void insertData(const WatchData &data);
|
||||||
void sendWatchParameters(const QByteArray ¶ms0);
|
void sendWatchParameters(const QByteArray ¶ms0);
|
||||||
void createGdbVariable(const WatchData &data);
|
void createGdbVariableClassic(const WatchData &data);
|
||||||
|
|
||||||
void runDebuggingHelper(const WatchData &data, bool dumpChildren);
|
void runDebuggingHelperClassic(const WatchData &data, bool dumpChildren);
|
||||||
void runDirectDebuggingHelper(const WatchData &data, bool dumpChildren);
|
void runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren);
|
||||||
bool hasDebuggingHelperForType(const QString &type) const;
|
bool hasDebuggingHelperForType(const QString &type) const;
|
||||||
|
|
||||||
void handleVarListChildren(const GdbResponse &response);
|
void handleVarListChildren(const GdbResponse &response);
|
||||||
@@ -442,20 +443,25 @@ private: ////////// View & Data Stuff //////////
|
|||||||
void handleVarAssign(const GdbResponse &response);
|
void handleVarAssign(const GdbResponse &response);
|
||||||
void handleEvaluateExpression(const GdbResponse &response);
|
void handleEvaluateExpression(const GdbResponse &response);
|
||||||
//void handleToolTip(const GdbResponse &response);
|
//void handleToolTip(const GdbResponse &response);
|
||||||
void handleQueryDebuggingHelper(const GdbResponse &response);
|
void handleQueryDebuggingHelperClassic(const GdbResponse &response);
|
||||||
void handleDebuggingHelperValue2(const GdbResponse &response);
|
void handleDebuggingHelperValue2Classic(const GdbResponse &response);
|
||||||
void handleDebuggingHelperValue3(const GdbResponse &response);
|
void handleDebuggingHelperValue3Classic(const GdbResponse &response);
|
||||||
void handleDebuggingHelperEditValue(const GdbResponse &response);
|
void handleDebuggingHelperEditValue(const GdbResponse &response);
|
||||||
void handleDebuggingHelperSetup(const GdbResponse &response);
|
void handleDebuggingHelperSetup(const GdbResponse &response);
|
||||||
|
|
||||||
void updateLocals(const QVariant &cookie = QVariant());
|
void updateLocals(const QVariant &cookie = QVariant());
|
||||||
void handleStackListLocals(const GdbResponse &response);
|
void updateLocalsClassic(const QVariant &cookie);
|
||||||
void handleStackListLocals0(const GdbResponse &response);
|
void updateLocalsPython(const QByteArray &varList);
|
||||||
|
void handleStackFramePython(const GdbResponse &response);
|
||||||
|
|
||||||
|
void handleStackListLocalsClassic(const GdbResponse &response);
|
||||||
|
void handleStackListLocalsPython(const GdbResponse &response);
|
||||||
|
|
||||||
WatchData localVariable(const GdbMi &item,
|
WatchData localVariable(const GdbMi &item,
|
||||||
const QStringList &uninitializedVariables,
|
const QStringList &uninitializedVariables,
|
||||||
QMap<QByteArray, int> *seen);
|
QMap<QByteArray, int> *seen);
|
||||||
void setLocals(const QList<GdbMi> &locals);
|
void setLocals(const QList<GdbMi> &locals);
|
||||||
void handleStackListArguments(const GdbResponse &response);
|
void handleStackListArgumentsClassic(const GdbResponse &response);
|
||||||
void setWatchDataType(WatchData &data, const GdbMi &mi);
|
void setWatchDataType(WatchData &data, const GdbMi &mi);
|
||||||
void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi);
|
void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi);
|
||||||
|
|
||||||
@@ -465,13 +471,14 @@ private: ////////// View & Data Stuff //////////
|
|||||||
private: ////////// Dumper Management //////////
|
private: ////////// Dumper Management //////////
|
||||||
QString qtDumperLibraryName() const;
|
QString qtDumperLibraryName() const;
|
||||||
bool checkDebuggingHelpers();
|
bool checkDebuggingHelpers();
|
||||||
void setDebuggingHelperState(DebuggingHelperState);
|
bool checkDebuggingHelpersClassic();
|
||||||
void tryLoadDebuggingHelpers();
|
void setDebuggingHelperStateClassic(DebuggingHelperState);
|
||||||
void tryQueryDebuggingHelpers();
|
void tryLoadDebuggingHelpersClassic();
|
||||||
Q_SLOT void recheckDebuggingHelperAvailability();
|
void tryQueryDebuggingHelpersClassic();
|
||||||
|
Q_SLOT void recheckDebuggingHelperAvailabilityClassic();
|
||||||
void connectDebuggingHelperActions();
|
void connectDebuggingHelperActions();
|
||||||
void disconnectDebuggingHelperActions();
|
void disconnectDebuggingHelperActions();
|
||||||
Q_SLOT void setDebugDebuggingHelpers(const QVariant &on);
|
Q_SLOT void setDebugDebuggingHelpersClassic(const QVariant &on);
|
||||||
Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
|
Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
|
||||||
|
|
||||||
const bool m_dumperInjectionLoad;
|
const bool m_dumperInjectionLoad;
|
||||||
@@ -486,7 +493,9 @@ private: ////////// Convenience Functions //////////
|
|||||||
void debugMessage(const QString &msg);
|
void debugMessage(const QString &msg);
|
||||||
QMainWindow *mainWindow() const;
|
QMainWindow *mainWindow() const;
|
||||||
|
|
||||||
void updateLocalsSync(const QByteArray &varList);
|
static QString m_toolTipExpression;
|
||||||
|
static QPoint m_toolTipPos;
|
||||||
|
static QByteArray tooltipINameForExpression(const QByteArray &exp);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
177
src/plugins/debugger/gdb/pythongdbengine.cpp
Normal file
177
src/plugins/debugger/gdb/pythongdbengine.cpp
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
**
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "gdbengine.h"
|
||||||
|
|
||||||
|
#include "abstractgdbadapter.h"
|
||||||
|
#include "debuggeractions.h"
|
||||||
|
#include "debuggerstringutils.h"
|
||||||
|
|
||||||
|
#include "watchhandler.h"
|
||||||
|
#include "stackhandler.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#define PRECONDITION QTC_ASSERT(hasPython(), /**/)
|
||||||
|
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
|
||||||
|
|
||||||
|
|
||||||
|
namespace Debugger {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
void GdbEngine::updateLocalsPython(const QByteArray &varList)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (m_gdbAdapter->isTrkAdapter()) {
|
||||||
|
postCommand("-stack-list-locals 0",
|
||||||
|
WatchUpdate, CB(handleStackListLocalsPython));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_processedNames.clear();
|
||||||
|
manager()->watchHandler()->beginCycle();
|
||||||
|
//m_toolTipExpression.clear();
|
||||||
|
WatchHandler *handler = m_manager->watchHandler();
|
||||||
|
|
||||||
|
QByteArray expanded;
|
||||||
|
QSet<QByteArray> expandedINames = handler->expandedINames();
|
||||||
|
QSetIterator<QByteArray> jt(expandedINames);
|
||||||
|
while (jt.hasNext()) {
|
||||||
|
expanded.append(jt.next());
|
||||||
|
expanded.append(',');
|
||||||
|
}
|
||||||
|
if (expanded.isEmpty())
|
||||||
|
expanded.append("defaults,");
|
||||||
|
expanded.chop(1);
|
||||||
|
|
||||||
|
QByteArray watchers;
|
||||||
|
if (!m_toolTipExpression.isEmpty())
|
||||||
|
watchers += m_toolTipExpression.toLatin1()
|
||||||
|
+ "#" + tooltipINameForExpression(m_toolTipExpression.toLatin1());
|
||||||
|
|
||||||
|
QHash<QByteArray, int> watcherNames = handler->watcherNames();
|
||||||
|
QHashIterator<QByteArray, int> it(watcherNames);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
if (!watchers.isEmpty())
|
||||||
|
watchers += "##";
|
||||||
|
if (it.key() == WatchHandler::watcherEditPlaceHolder().toLatin1())
|
||||||
|
watchers += "<Edit>#watch." + QByteArray::number(it.value());
|
||||||
|
else
|
||||||
|
watchers += it.key() + "#watch." + QByteArray::number(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray options;
|
||||||
|
if (theDebuggerBoolSetting(UseDebuggingHelpers))
|
||||||
|
options += "fancy,";
|
||||||
|
if (theDebuggerBoolSetting(AutoDerefPointers))
|
||||||
|
options += "autoderef,";
|
||||||
|
if (options.isEmpty())
|
||||||
|
options += "defaults,";
|
||||||
|
options.chop(1);
|
||||||
|
|
||||||
|
postCommand("bb " + options + " @" + varList + ' '
|
||||||
|
+ expanded + ' ' + watchers.toHex(),
|
||||||
|
WatchUpdate, CB(handleStackFramePython));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleStackListLocalsPython(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (response.resultClass == GdbResultDone) {
|
||||||
|
// 44^done,data={locals=[name="model",name="backString",...]}
|
||||||
|
QByteArray varList = "vars"; // Dummy entry, will be stripped by dumper.
|
||||||
|
foreach (const GdbMi &child, response.data.findChild("locals").children()) {
|
||||||
|
varList.append(',');
|
||||||
|
varList.append(child.data());
|
||||||
|
}
|
||||||
|
updateLocalsPython(varList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleStackFramePython(const GdbResponse &response)
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
if (response.resultClass == GdbResultDone) {
|
||||||
|
QByteArray out = response.data.findChild("consolestreamoutput").data();
|
||||||
|
while (out.endsWith(' ') || out.endsWith('\n'))
|
||||||
|
out.chop(1);
|
||||||
|
//qDebug() << "SECOND CHUNK: " << out;
|
||||||
|
int pos = out.indexOf("data=");
|
||||||
|
if (pos != 0) {
|
||||||
|
qDebug() << "DISCARDING JUNK AT BEGIN OF RESPONSE: "
|
||||||
|
<< out.left(pos);
|
||||||
|
out = out.mid(pos);
|
||||||
|
}
|
||||||
|
GdbMi all;
|
||||||
|
all.fromStringMultiple(out);
|
||||||
|
//qDebug() << "ALL: " << all.toString();
|
||||||
|
|
||||||
|
GdbMi data = all.findChild("data");
|
||||||
|
QList<WatchData> list;
|
||||||
|
foreach (const GdbMi &child, data.children()) {
|
||||||
|
WatchData dummy;
|
||||||
|
dummy.iname = child.findChild("iname").data();
|
||||||
|
dummy.name = _(child.findChild("name").data());
|
||||||
|
//qDebug() << "CHILD: " << child.toString();
|
||||||
|
handleChildren(dummy, child, &list);
|
||||||
|
}
|
||||||
|
manager()->watchHandler()->insertBulkData(list);
|
||||||
|
//for (int i = 0; i != list.size(); ++i)
|
||||||
|
// qDebug() << "LOCAL: " << list.at(i).toString();
|
||||||
|
|
||||||
|
//PENDING_DEBUG("AFTER handleStackFrame()");
|
||||||
|
// FIXME: This should only be used when updateLocals() was
|
||||||
|
// triggered by expanding an item in the view.
|
||||||
|
if (m_pendingRequests <= 0) {
|
||||||
|
//PENDING_DEBUG("\n\n .... AND TRIGGERS MODEL UPDATE\n");
|
||||||
|
rebuildModel();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugMessage(_("DUMPER FAILED: " + response.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from CoreAdapter and AttachAdapter
|
||||||
|
void GdbEngine::updateAllPython()
|
||||||
|
{
|
||||||
|
PRECONDITION;
|
||||||
|
//PENDING_DEBUG("UPDATING ALL\n");
|
||||||
|
QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
|
||||||
|
reloadModulesInternal();
|
||||||
|
postCommand("-stack-list-frames", WatchUpdate, CB(handleStackListFrames),
|
||||||
|
QVariant::fromValue<StackCookie>(StackCookie(false, true)));
|
||||||
|
manager()->stackHandler()->setCurrentIndex(0);
|
||||||
|
if (supportsThreads())
|
||||||
|
postCommand("-thread-list-ids", WatchUpdate, CB(handleStackListThreads), 0);
|
||||||
|
manager()->reloadRegisters();
|
||||||
|
updateLocals();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Debugger
|
||||||
@@ -100,7 +100,7 @@ void TermGdbAdapter::startAdapter()
|
|||||||
var += QLatin1Char('=');
|
var += QLatin1Char('=');
|
||||||
var += m_engine->qtDumperLibraryName();
|
var += m_engine->qtDumperLibraryName();
|
||||||
environment.push_back(var);
|
environment.push_back(var);
|
||||||
m_engine->setDebuggingHelperState(DebuggingHelperLoadTried);
|
m_engine->setDebuggingHelperStateClassic(DebuggingHelperLoadTried);
|
||||||
}
|
}
|
||||||
m_stubProc.setEnvironment(environment);
|
m_stubProc.setEnvironment(environment);
|
||||||
// FIXME: Starting the stub implies starting the inferior. This is
|
// FIXME: Starting the stub implies starting the inferior. This is
|
||||||
|
|||||||
Reference in New Issue
Block a user