summaryrefslogtreecommitdiff
path: root/src/plugins/debugger/gdb
diff options
context:
space:
mode:
authorhjk <qtc-committer@nokia.com>2010-01-20 15:38:01 +0100
committerhjk <qtc-committer@nokia.com>2010-01-21 17:19:08 +0100
commitcdae30ccdbdaac8511ba7fdba5c72a55d61e523e (patch)
tree3b68af11b8e755358be1ac7a26afabeeb5b691f7 /src/plugins/debugger/gdb
parentba877b63699713d0ba0915a7bb0f95d8694925b4 (diff)
downloadqt-creator-cdae30ccdbdaac8511ba7fdba5c72a55d61e523e.tar.gz
debugger: start splitting off python and non-python specific bits
Diffstat (limited to 'src/plugins/debugger/gdb')
-rw-r--r--src/plugins/debugger/gdb/classicgdbengine.cpp761
-rw-r--r--src/plugins/debugger/gdb/gdb.pri2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp849
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h49
-rw-r--r--src/plugins/debugger/gdb/pythongdbengine.cpp177
-rw-r--r--src/plugins/debugger/gdb/termgdbadapter.cpp2
6 files changed, 1015 insertions, 825 deletions
diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp
new file mode 100644
index 0000000000..20f15288e2
--- /dev/null
+++ b/src/plugins/debugger/gdb/classicgdbengine.cpp
@@ -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, &params, &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
diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri
index 1c969a42b8..ad27702938 100644
--- a/src/plugins/debugger/gdb/gdb.pri
+++ b/src/plugins/debugger/gdb/gdb.pri
@@ -19,6 +19,8 @@ HEADERS += \
SOURCES += \
$$PWD/gdbmi.cpp \
$$PWD/gdbengine.cpp \
+ $$PWD/classicgdbengine.cpp \
+ $$PWD/pythongdbengine.cpp \
$$PWD/gdboptionspage.cpp \
$$PWD/trkoptions.cpp \
$$PWD/trkoptionswidget.cpp \
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 0379837e40..7d25d9d00d 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -90,11 +90,18 @@
#endif
#include <ctype.h>
+#define DIVERT(func, pars) \
+ do { \
+ if (hasPython()) \
+ func ## Python(pars); \
+ else \
+ func ## Plain(pars); \
+ } while (0)
+
namespace Debugger {
namespace Internal {
//#define DEBUG_PENDING 1
-//#define DEBUG_SUBITEM 1
#if DEBUG_PENDING
# define PENDING_DEBUG(s) qDebug() << s
@@ -105,7 +112,7 @@ namespace Internal {
#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
//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)
{
GdbMi output = response.data.findChild("consolestreamoutput");
@@ -276,7 +261,7 @@ void GdbEngine::initializeVariables()
m_gdbVersion = 100;
m_gdbBuildVersion = -1;
m_isMacGdb = false;
- m_isSynchronous = false;
+ m_hasPython = false;
m_registerNamesListed = false;
m_fullToShortName.clear();
@@ -698,8 +683,8 @@ void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
debugMessage(_("FOUND PID %1").arg(pid));
handleInferiorPidChanged(pid);
- if (m_dumperInjectionLoad)
- tryLoadDebuggingHelpers();
+ if (m_dumperInjectionLoad && !hasPython())
+ tryLoadDebuggingHelpersClassic();
}
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
@@ -1023,17 +1008,10 @@ void GdbEngine::executeDebuggerCommand(const QString &command)
// Called from CoreAdapter and AttachAdapter
void GdbEngine::updateAll()
{
- PENDING_DEBUG("UPDATING ALL\n");
- QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
- tryLoadDebuggingHelpers();
- 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();
+ if (hasPython())
+ updateAllPython();
+ else
+ updateAllClassic();
}
void GdbEngine::handleQuerySources(const GdbResponse &response)
@@ -1324,7 +1302,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
if (isSynchroneous())
initHelpers = false;
if (initHelpers) {
- tryLoadDebuggingHelpers();
+ tryLoadDebuggingHelpersClassic();
QVariant var = QVariant::fromValue<GdbMi>(data);
postCommand("p 4", CB(handleStop1), var); // dummy
} 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);
if (response.resultClass == GdbResultDone) {
- m_isSynchronous = true;
+ m_hasPython = true;
} else {
- m_isSynchronous = false;
+ m_hasPython = false;
}
}
@@ -1867,19 +1845,6 @@ void GdbEngine::setTokenBarrier()
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;
-static QPoint m_toolTipPos;
+QString GdbEngine::m_toolTipExpression;
+QPoint GdbEngine::m_toolTipPos;
bool GdbEngine::showToolTip()
{
@@ -2934,14 +2899,6 @@ static void setWatchDataSAddress(WatchData &data, const GdbMi &mi)
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)
{
Q_UNUSED(on)
@@ -2971,259 +2928,6 @@ bool GdbEngine::hasDebuggingHelperForType(const QString &type) const
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, &params, &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)
{
@@ -3281,7 +2985,7 @@ void GdbEngine::updateWatchDataHelper(const WatchData &data)
//qDebug() << data.toString();
#endif
- updateSubItem(data);
+ updateSubItemClassic(data);
//PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
--m_pendingRequests;
PENDING_DEBUG("UPDATE WATCH DONE BUMPS PENDING DOWN TO " << m_pendingRequests);
@@ -3302,51 +3006,7 @@ void GdbEngine::rebuildModel()
showToolTip();
}
-static inline 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::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 &params)
+static QByteArray arrayFillCommand(const char *array, const QByteArray &params)
{
char buf[50];
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,
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)
{
m_pendingRequests = 0;
- if (isSynchroneous()) {
- if (m_gdbAdapter->isTrkAdapter()) {
- 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
- }
+ if (hasPython())
+ updateLocalsPython(QByteArray());
+ else
+ updateLocalsClassic(cookie);
}
-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
- 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(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
WatchData GdbEngine::localVariable(const GdbMi &item,
@@ -4104,134 +3460,6 @@ QString GdbEngine::qtDumperLibraryName() const
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)
{
//qDebug() << "WATCH " << pnt;
@@ -4530,7 +3758,7 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr
NonCriticalResponse);
postCommand("-interpreter-exec console \"help bb\"",
- CB(handleIsSynchroneous));
+ CB(handleHasPython));
//postCommand("-enable-timings");
postCommand("set print static-members off"); // Seemingly doesn't work.
//postCommand("set debug infrun 1");
@@ -4611,7 +3839,7 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr
}
}
if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperLoadedByGdbPreload
- && checkDebuggingHelpers()) {
+ && checkDebuggingHelpers()) {
QByteArray cmd = "set environment ";
cmd += Debugger::Constants::Internal::LD_PRELOAD_ENV_VAR;
cmd += ' ';
@@ -4622,6 +3850,11 @@ bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QStr
return true;
}
+bool GdbEngine::checkDebuggingHelpers()
+{
+ return !hasPython() && checkDebuggingHelpersClassic();
+}
+
void GdbEngine::handleGdbError(QProcess::ProcessError 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);
}
-bool GdbEngine::isSynchroneous() const
+void GdbEngine::setUseDebuggingHelpers(const QVariant &on)
+{
+ //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
+ Q_UNUSED(on)
+ setTokenBarrier();
+ updateLocals();
+}
+
+bool GdbEngine::hasPython() const
{
- return m_isSynchronous;
+ return m_hasPython;
}
//
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 8123827b63..da0be04d5c 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -264,17 +264,17 @@ private: ////////// Gdb Output, State & Capability Handling //////////
void handleStop1(const GdbMi &data);
StackFrame parseStackFrame(const GdbMi &mi, int level);
- virtual bool isSynchroneous() const;
+ virtual bool hasPython() const;
bool supportsThreads() const;
// Gdb initialization sequence
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_gdbBuildVersion; // MAC only?
bool m_isMacGdb;
- bool m_isSynchronous; // Can act synchronously?
+ bool m_hasPython;
private: ////////// Inferior Management //////////
@@ -393,10 +393,11 @@ private: ////////// View & Data Stuff //////////
// Stack specific stuff
//
void updateAll();
+ void updateAllClassic();
+ void updateAllPython();
void handleStackListFrames(const GdbResponse &response);
void handleStackSelectThread(const GdbResponse &response);
void handleStackListThreads(const GdbResponse &response);
- void handleStackFrame(const GdbResponse &response);
Q_SLOT void reloadStack(bool forceGotoLocation);
Q_SLOT virtual void reloadFullStack();
int currentFrame() const;
@@ -418,7 +419,7 @@ private: ////////// View & Data Stuff //////////
void handleWatchPoint(const GdbResponse &response);
// 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,
QList<WatchData> *insertions);
@@ -429,10 +430,10 @@ private: ////////// View & Data Stuff //////////
void insertData(const WatchData &data);
void sendWatchParameters(const QByteArray &params0);
- void createGdbVariable(const WatchData &data);
+ void createGdbVariableClassic(const WatchData &data);
- void runDebuggingHelper(const WatchData &data, bool dumpChildren);
- void runDirectDebuggingHelper(const WatchData &data, bool dumpChildren);
+ void runDebuggingHelperClassic(const WatchData &data, bool dumpChildren);
+ void runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren);
bool hasDebuggingHelperForType(const QString &type) const;
void handleVarListChildren(const GdbResponse &response);
@@ -442,20 +443,25 @@ private: ////////// View & Data Stuff //////////
void handleVarAssign(const GdbResponse &response);
void handleEvaluateExpression(const GdbResponse &response);
//void handleToolTip(const GdbResponse &response);
- void handleQueryDebuggingHelper(const GdbResponse &response);
- void handleDebuggingHelperValue2(const GdbResponse &response);
- void handleDebuggingHelperValue3(const GdbResponse &response);
+ void handleQueryDebuggingHelperClassic(const GdbResponse &response);
+ void handleDebuggingHelperValue2Classic(const GdbResponse &response);
+ void handleDebuggingHelperValue3Classic(const GdbResponse &response);
void handleDebuggingHelperEditValue(const GdbResponse &response);
void handleDebuggingHelperSetup(const GdbResponse &response);
void updateLocals(const QVariant &cookie = QVariant());
- void handleStackListLocals(const GdbResponse &response);
- void handleStackListLocals0(const GdbResponse &response);
+ void updateLocalsClassic(const QVariant &cookie);
+ 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,
const QStringList &uninitializedVariables,
QMap<QByteArray, int> *seen);
void setLocals(const QList<GdbMi> &locals);
- void handleStackListArguments(const GdbResponse &response);
+ void handleStackListArgumentsClassic(const GdbResponse &response);
void setWatchDataType(WatchData &data, const GdbMi &mi);
void setWatchDataDisplayedType(WatchData &data, const GdbMi &mi);
@@ -465,13 +471,14 @@ private: ////////// View & Data Stuff //////////
private: ////////// Dumper Management //////////
QString qtDumperLibraryName() const;
bool checkDebuggingHelpers();
- void setDebuggingHelperState(DebuggingHelperState);
- void tryLoadDebuggingHelpers();
- void tryQueryDebuggingHelpers();
- Q_SLOT void recheckDebuggingHelperAvailability();
+ bool checkDebuggingHelpersClassic();
+ void setDebuggingHelperStateClassic(DebuggingHelperState);
+ void tryLoadDebuggingHelpersClassic();
+ void tryQueryDebuggingHelpersClassic();
+ Q_SLOT void recheckDebuggingHelperAvailabilityClassic();
void connectDebuggingHelperActions();
void disconnectDebuggingHelperActions();
- Q_SLOT void setDebugDebuggingHelpers(const QVariant &on);
+ Q_SLOT void setDebugDebuggingHelpersClassic(const QVariant &on);
Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
const bool m_dumperInjectionLoad;
@@ -486,7 +493,9 @@ private: ////////// Convenience Functions //////////
void debugMessage(const QString &msg);
QMainWindow *mainWindow() const;
- void updateLocalsSync(const QByteArray &varList);
+ static QString m_toolTipExpression;
+ static QPoint m_toolTipPos;
+ static QByteArray tooltipINameForExpression(const QByteArray &exp);
};
} // namespace Internal
diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp
new file mode 100644
index 0000000000..8f57cd430a
--- /dev/null
+++ b/src/plugins/debugger/gdb/pythongdbengine.cpp
@@ -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
diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp
index 6b096b2830..768c004c09 100644
--- a/src/plugins/debugger/gdb/termgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/termgdbadapter.cpp
@@ -100,7 +100,7 @@ void TermGdbAdapter::startAdapter()
var += QLatin1Char('=');
var += m_engine->qtDumperLibraryName();
environment.push_back(var);
- m_engine->setDebuggingHelperState(DebuggingHelperLoadTried);
+ m_engine->setDebuggingHelperStateClassic(DebuggingHelperLoadTried);
}
m_stubProc.setEnvironment(environment);
// FIXME: Starting the stub implies starting the inferior. This is