diff options
author | hjk <qtc-committer@nokia.com> | 2010-01-20 15:38:01 +0100 |
---|---|---|
committer | hjk <qtc-committer@nokia.com> | 2010-01-21 17:19:08 +0100 |
commit | cdae30ccdbdaac8511ba7fdba5c72a55d61e523e (patch) | |
tree | 3b68af11b8e755358be1ac7a26afabeeb5b691f7 /src/plugins/debugger/gdb | |
parent | ba877b63699713d0ba0915a7bb0f95d8694925b4 (diff) | |
download | qt-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.cpp | 761 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdb.pri | 2 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 849 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.h | 49 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/pythongdbengine.cpp | 177 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/termgdbadapter.cpp | 2 |
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, ¶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 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, ¶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) { @@ -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 ¶ms) +static QByteArray arrayFillCommand(const char *array, const QByteArray ¶ms) { 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 ¶ms0); - 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 |