diff options
Diffstat (limited to 'src/plugins/debugger/gdb')
-rw-r--r-- | src/plugins/debugger/gdb/abstractplaingdbadapter.cpp | 6 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/attachgdbadapter.cpp | 8 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/classicgdbengine.cpp | 24 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/coregdbadapter.cpp | 9 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdb.pri | 2 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 279 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.h | 19 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbmi.cpp | 433 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbmi.h | 180 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdboptionspage.cpp | 55 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/localplaingdbadapter.cpp | 8 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/pythongdbengine.cpp | 3 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/remotegdbserveradapter.cpp | 38 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/termgdbadapter.cpp | 55 |
14 files changed, 334 insertions, 785 deletions
diff --git a/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp b/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp index 0c1de8b3d7..305d68d71a 100644 --- a/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp +++ b/src/plugins/debugger/gdb/abstractplaingdbadapter.cpp @@ -28,11 +28,11 @@ ****************************************************************************/ #include "abstractplaingdbadapter.h" -#include "gdbmi.h" -#include "gdbengine.h" -#include "debuggerstartparameters.h" + #include "debuggeractions.h" #include "debuggercore.h" +#include "debuggerprotocol.h" +#include "debuggerstartparameters.h" #include "debuggerstringutils.h" #include <QDir> diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp index 4b71ba73fa..9467471bd9 100644 --- a/src/plugins/debugger/gdb/attachgdbadapter.cpp +++ b/src/plugins/debugger/gdb/attachgdbadapter.cpp @@ -28,12 +28,11 @@ ****************************************************************************/ #include "attachgdbadapter.h" -#include "gdbmi.h" -#include "debuggerstartparameters.h" -#include "gdbengine.h" -#include "procinterrupt.h" +#include "debuggerprotocol.h" #include "debuggerstringutils.h" +#include "debuggerstartparameters.h" +#include "procinterrupt.h" #include <utils/qtcassert.h> @@ -88,6 +87,7 @@ void GdbAttachEngine::handleAttach(const GdbResponse &response) case GdbResultRunning: showMessage(_("INFERIOR ATTACHED")); showMessage(msgAttachedToStoppedInferior(), StatusBar); + tryLoadPythonDumpers(); handleInferiorPrepared(); break; case GdbResultError: diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp index c96735eeab..897ead3f3f 100644 --- a/src/plugins/debugger/gdb/classicgdbengine.cpp +++ b/src/plugins/debugger/gdb/classicgdbengine.cpp @@ -28,13 +28,13 @@ ****************************************************************************/ #include "gdbengine.h" -#include "gdbmi.h" -#include "debuggerstartparameters.h" #include "debuggeractions.h" #include "debuggercore.h" +#include "debuggerprotocol.h" +#include "debuggerstartparameters.h" #include "debuggerstringutils.h" - +#include "sourceutils.h" #include "stackhandler.h" #include "watchhandler.h" @@ -552,11 +552,10 @@ void DumperHelper::evaluationParameters(const WatchData &data, // in rare cases we need more or less: switch (td.type) { case QAbstractItemType: - if (data.dumperFlags.isEmpty()) { + if (data.dumperFlags.isEmpty()) qWarning("Internal error: empty dumper state '%s'.", data.iname.constData()); - } else { + else inner = data.dumperFlags.mid(1); - } break; case QObjectSlotType: case QObjectSignalType: { @@ -1068,11 +1067,10 @@ void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response) data1.type = data.type.left(data.type.size() - 4); data1.iname = data.iname + '.' + QByteArray::number(i); const QByteArray &addressSpec = list.at(i); - if (addressSpec.startsWith("0x")) { + if (addressSpec.startsWith("0x")) data.setHexAddress(addressSpec); - } else { + else data.dumperFlags = addressSpec; // Item model dumpers pull tricks - } data1.exp = "((" + gdbQuoteTypes(data1.type) + "*)" + addressSpec + ')'; data1.setHasChildren(false); data1.setValueNeeded(); @@ -1230,8 +1228,8 @@ void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response) QStringList uninitializedVariables; if (debuggerCore()->action(UseCodeModel)->isChecked()) { const StackFrame frame = - qVariantCanConvert<Debugger::Internal::StackFrame>(response.cookie) - ? qVariantValue<Debugger::Internal::StackFrame>(response.cookie) + response.cookie.canConvert<Debugger::Internal::StackFrame>() + ? qvariant_cast<Debugger::Internal::StackFrame>(response.cookie) : stackHandler()->currentFrame(); if (frame.isUsable()) getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(), @@ -1284,8 +1282,8 @@ static void showQtDumperLibraryWarning(const QString &details) dialog.exec(); if (dialog.clickedButton() == qtPref) { Core::ICore::showOptionsDialog( - _(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY), - _(QtSupport::Constants::QTVERSION_SETTINGS_PAGE_ID)); + ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY, + QtSupport::Constants::QTVERSION_SETTINGS_PAGE_ID); } else if (dialog.clickedButton() == helperOff) { debuggerCore()->action(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false); } diff --git a/src/plugins/debugger/gdb/coregdbadapter.cpp b/src/plugins/debugger/gdb/coregdbadapter.cpp index fc782d8c64..71f3f4657c 100644 --- a/src/plugins/debugger/gdb/coregdbadapter.cpp +++ b/src/plugins/debugger/gdb/coregdbadapter.cpp @@ -29,12 +29,11 @@ #include "coregdbadapter.h" -#include "debuggerstartparameters.h" -#include "debuggercore.h" #include "debuggeractions.h" +#include "debuggercore.h" +#include "debuggerprotocol.h" +#include "debuggerstartparameters.h" #include "debuggerstringutils.h" -#include "gdbmi.h" -#include "gdbengine.h" #include <utils/consoleprocess.h> #include <utils/elfreader.h> @@ -185,7 +184,7 @@ void GdbCoreEngine::handleTargetCore(const GdbResponse &response) QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); if (response.resultClass == GdbResultDone) { // HACK: The namespace is not accessible in the initial run. - loadPythonDumpers(); + tryLoadPythonDumpers(); showMessage(tr("Attached to core."), StatusBar); handleInferiorPrepared(); // Due to the auto-solib-add off setting, we don't have any diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri index b371e63e73..7a100e6851 100644 --- a/src/plugins/debugger/gdb/gdb.pri +++ b/src/plugins/debugger/gdb/gdb.pri @@ -1,5 +1,4 @@ HEADERS += \ - $$PWD/gdbmi.h \ $$PWD/gdbengine.h \ $$PWD/gdboptionspage.h \ $$PWD/attachgdbadapter.h \ @@ -15,7 +14,6 @@ HEADERS += \ $$PWD/startgdbserverdialog.h SOURCES += \ - $$PWD/gdbmi.cpp \ $$PWD/gdbengine.cpp \ $$PWD/classicgdbengine.cpp \ $$PWD/pythongdbengine.cpp \ diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index f472cf96e7..f939934234 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -44,13 +44,14 @@ #include "debuggerconstants.h" #include "debuggercore.h" #include "debuggerplugin.h" +#include "debuggerprotocol.h" #include "debuggerrunner.h" #include "debuggerstringutils.h" #include "debuggertooltipmanager.h" #include "disassembleragent.h" -#include "gdbmi.h" #include "gdboptionspage.h" #include "memoryagent.h" +#include "sourceutils.h" #include "watchutils.h" #include "breakhandler.h" @@ -75,6 +76,7 @@ #include <projectexplorer/itaskhandler.h> #include <texteditor/itexteditor.h> #include <utils/elfreader.h> +#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/savedaction.h> @@ -261,6 +263,7 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters) m_terminalTrap = startParameters.useTerminal; m_fullStartDone = false; m_forceAsyncModel = false; + m_pythonAttemptedToLoad = false; invalidateSourcesList(); @@ -278,6 +281,8 @@ GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters) SLOT(reloadLocals())); connect(debuggerCore()->action(UseDynamicType), SIGNAL(valueChanged(QVariant)), SLOT(reloadLocals())); + connect(debuggerCore()->action(IntelFlavor), SIGNAL(valueChanged(QVariant)), + SLOT(reloadDisassembly())); } GdbEngine::~GdbEngine() @@ -343,24 +348,23 @@ static void dump(const char *first, const char *middle, const QString & to) // Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n" // and return an exception message -static inline QString msgWinException(const QByteArray &data) +static inline QString msgWinException(const QByteArray &data, unsigned *exCodeIn = 0) { + if (exCodeIn) + *exCodeIn = 0; const int exCodePos = data.indexOf("0x"); const int blankPos = exCodePos != -1 ? data.indexOf(' ', exCodePos + 1) : -1; const int addressPos = blankPos != -1 ? data.indexOf("0x", blankPos + 1) : -1; if (addressPos < 0) return GdbEngine::tr("An exception was triggered."); const unsigned exCode = data.mid(exCodePos, blankPos - exCodePos).toUInt(0, 0); + if (exCodeIn) + *exCodeIn = exCode; const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0); QString rc; QTextStream str(&rc); str << GdbEngine::tr("An exception was triggered: "); -#ifdef Q_OS_WIN formatWindowsException(exCode, address, 0, 0, 0, str); -#else - Q_UNUSED(exCode) - Q_UNUSED(address) -#endif str << '.'; return rc; } @@ -466,6 +470,8 @@ void GdbEngine::handleResponse(const QByteArray &buff) m_pendingLogStreamOutput.clear(); m_pendingConsoleStreamOutput.clear(); } else if (asyncClass == "running") { + GdbMi threads = result.findChild("thread-id"); + threadsHandler()->notifyRunning(threads.data()); if (state() == InferiorRunOk || state() == InferiorSetupRequested) { // We get multiple *running after thread creation and in Windows terminals. showMessage(QString::fromLatin1("NOTE: INFERIOR STILL RUNNING IN STATE %1."). @@ -526,6 +532,10 @@ void GdbEngine::handleResponse(const QByteArray &buff) //"{id="1",group-id="28902"}" QByteArray id = result.findChild("id").data(); showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000); + ThreadData thread; + thread.id = ThreadId(id.toLong()); + thread.groupId = result.findChild("group-id").data(); + threadsHandler()->updateThread(thread); } else if (asyncClass == "thread-group-exited") { // Archer has "{id="28902"}" QByteArray id = result.findChild("id").data(); @@ -537,6 +547,7 @@ void GdbEngine::handleResponse(const QByteArray &buff) QByteArray groupid = result.findChild("group-id").data(); showStatusMessage(tr("Thread %1 in group %2 exited") .arg(_(id)).arg(_(groupid)), 1000); + threadsHandler()->removeThread(ThreadId(id.toLong())); } else if (asyncClass == "thread-selected") { QByteArray id = result.findChild("id").data(); showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000); @@ -689,8 +700,13 @@ void GdbEngine::handleResponse(const QByteArray &buff) // [Windows, most likely some DLL/Entry point not found]: // "gdb: unknown target exception 0xc0000139 at 0x77bef04e" // This may be fatal and cause the target to exit later - m_lastWinException = msgWinException(data); + unsigned exCode; + m_lastWinException = msgWinException(data, &exCode); showMessage(m_lastWinException, LogMisc); + const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning; + const Task task(type, m_lastWinException, Utils::FileName(), 0, + Core::Id(Debugger::Constants::TASK_CATEGORY_DEBUGGER_RUNTIME)); + taskHub()->addTask(task); } if (data.startsWith("QMLBP:")) { @@ -732,7 +748,7 @@ void GdbEngine::handleResponse(const QByteArray &buff) Task task(Task::Warning, tr("Missing debug information for %1\nTry: %2") .arg(m_lastMissingDebugInfo).arg(cmd), - FileName(), 0, Core::Id("Debuginfo")); + FileName(), 0, Core::Id(Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO)); taskHub()->addTask(task); @@ -754,19 +770,18 @@ void GdbEngine::handleResponse(const QByteArray &buff) break; QByteArray resultClass = QByteArray::fromRawData(from, inner - from); - if (resultClass == "done") { + if (resultClass == "done") response.resultClass = GdbResultDone; - } else if (resultClass == "running") { + else if (resultClass == "running") response.resultClass = GdbResultRunning; - } else if (resultClass == "connected") { + else if (resultClass == "connected") response.resultClass = GdbResultConnected; - } else if (resultClass == "error") { + else if (resultClass == "error") response.resultClass = GdbResultError; - } else if (resultClass == "exit") { + else if (resultClass == "exit") response.resultClass = GdbResultExit; - } else { + else response.resultClass = GdbResultUnknown; - } from = inner; if (from != to) { @@ -944,9 +959,8 @@ void GdbEngine::postCommandHelper(const GdbCommand &cmd) showMessage(_("QUEUING COMMAND " + cmd.command)); m_commandsToRunOnTemporaryBreak.append(cmd); if (state() == InferiorStopRequested) { - if (cmd.flags & LosesChild) { + if (cmd.flags & LosesChild) notifyInferiorIll(); - } showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING.")); // Calling shutdown() here breaks all situations where two // NeedsStop commands are issued in quick succession. @@ -1396,6 +1410,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data) return; } + GdbMi threads = data.findChild("stopped-thread"); + threadsHandler()->notifyStopped(threads.data()); + const QByteArray reason = data.findChild("reason").data(); if (isExitedReason(reason)) { @@ -1418,11 +1435,12 @@ void GdbEngine::handleStopResponse(const GdbMi &data) return; } + tryLoadPythonDumpers(); + bool gotoHandleStop1 = true; if (!m_fullStartDone) { m_fullStartDone = true; postCommand("sharedlibrary .*"); - loadPythonDumpers(); postCommand("p 3", CB(handleStop1)); gotoHandleStop1 = false; } @@ -1747,10 +1765,6 @@ void GdbEngine::handleStop2(const GdbMi &data) // Let the event loop run before deciding whether to update the stack. m_stackNeeded = true; // setTokenBarrier() might reset this. - if (isStopperThread) - m_currentThreadId = 0; - else - m_currentThreadId = data.findChild("thread-id").data().toInt(); QTimer::singleShot(0, this, SLOT(handleStop2())); } @@ -1760,8 +1774,6 @@ void GdbEngine::handleStop2() if (!m_stackNeeded) return; - reloadStack(false); // Will trigger register reload. - if (supportsThreads()) { if (m_isMacGdb || m_gdbVersion < 70100) { postCommand("-thread-list-ids", Discardable, CB(handleThreadListIds)); @@ -1770,7 +1782,6 @@ void GdbEngine::handleStop2() postCommand("-thread-info", Discardable, CB(handleThreadInfo)); } } - } void GdbEngine::handleInfoProc(const GdbResponse &response) @@ -1831,6 +1842,14 @@ void GdbEngine::handleListFeatures(const GdbResponse &response) void GdbEngine::handleHasPython(const GdbResponse &response) { + if (response.resultClass == GdbResultDone) + m_hasPython = true; + else + pythonDumpersFailed(); +} + +void GdbEngine::handlePythonSetup(const GdbResponse &response) +{ if (response.resultClass == GdbResultDone) { m_hasPython = true; GdbMi data; @@ -1852,8 +1871,6 @@ void GdbEngine::handleHasPython(const GdbResponse &response) } const GdbMi hasInferiorThreadList = data.findChild("hasInferiorThreadList"); m_hasInferiorThreadList = (hasInferiorThreadList.data().toInt() != 0); - } else { - pythonDumpersFailed(); } } @@ -2120,9 +2137,11 @@ bool GdbEngine::hasCapability(unsigned cap) const | AddWatcherCapability | WatchWidgetsCapability | ShowModuleSymbolsCapability + | ShowModuleSectionsCapability | CatchCapability | OperateByInstructionCapability | RunToLineCapability + | WatchComplexExpressionsCapability | MemoryAddressCapability)) return true; @@ -2166,11 +2185,10 @@ void GdbEngine::executeStep() setTokenBarrier(); notifyInferiorRunRequested(); showStatusMessage(tr("Step requested..."), 5000); - if (isReverseDebugging()) { + if (isReverseDebugging()) postCommand("reverse-step", RunRequest, CB(handleExecuteStep)); - } else { + else postCommand("-exec-step", RunRequest, CB(handleExecuteStep)); - } } void GdbEngine::handleExecuteStep(const GdbResponse &response) @@ -3317,7 +3335,7 @@ void GdbEngine::handleShowModuleSymbols(const GdbResponse &response) const QString modulePath = cookie.section(QLatin1Char('@'), 0, 0); const QString fileName = cookie.section(QLatin1Char('@'), 1, 1); if (response.resultClass == GdbResultDone) { - Symbols rc; + Symbols symbols; QFile file(fileName); file.open(QIODevice::ReadOnly); // Object file /opt/dev/qt/lib/libQtNetworkMyns.so.4: @@ -3361,17 +3379,62 @@ void GdbEngine::handleShowModuleSymbols(const GdbResponse &response) symbol.name = _(line.mid(posName, lenName)); symbol.section = _(line.mid(posSection, lenSection)); symbol.demangled = _(line.mid(posDemangled, lenDemangled)); - rc.push_back(symbol); + symbols.push_back(symbol); } file.close(); file.remove(); - debuggerCore()->showModuleSymbols(modulePath, rc); + debuggerCore()->showModuleSymbols(modulePath, symbols); } else { showMessageBox(QMessageBox::Critical, tr("Cannot Read Symbols"), tr("Cannot read symbols for module \"%1\".").arg(fileName)); } } +void GdbEngine::requestModuleSections(const QString &moduleName) +{ + // There seems to be no way to get the symbols from a single .so. + postCommand("maint info section ALLOBJ", + NeedsStop, CB(handleShowModuleSections), moduleName); +} + +void GdbEngine::handleShowModuleSections(const GdbResponse &response) +{ + // ~" Object file: /usr/lib/i386-linux-gnu/libffi.so.6\n" + // ~" 0xb44a6114->0xb44a6138 at 0x00000114: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS\n" + if (response.resultClass == GdbResultDone) { + const QString moduleName = response.cookie.toString(); + const QStringList lines = QString::fromLocal8Bit(response.consoleStreamOutput).split(QLatin1Char('\n')); + const QString prefix = QLatin1String(" Object file: "); + const QString needle = prefix + moduleName; + Sections sections; + bool active = false; + foreach (const QString &line, lines) { + if (line.startsWith(prefix)) { + if (active) + break; + if (line == needle) + active = true; + } else { + if (active) { + QStringList items = line.split(QLatin1Char(' '), QString::SkipEmptyParts); + QString fromTo = items.value(0, QString()); + const int pos = fromTo.indexOf(QLatin1Char('-')); + QTC_ASSERT(pos >= 0, continue); + Section section; + section.from = fromTo.left(pos); + section.to = fromTo.mid(pos + 2); + section.address = items.value(2, QString()); + section.name = items.value(3, QString()); + section.flags = items.value(4, QString()); + sections.append(section); + } + } + } + if (!sections.isEmpty()) + debuggerCore()->showModuleSections(moduleName, sections); + } +} + void GdbEngine::reloadModules() { if (state() == InferiorRunOk || state() == InferiorStopOk) @@ -3488,15 +3551,12 @@ void GdbEngine::reloadSourceFilesInternal() // ////////////////////////////////////////////////////////////////////// -void GdbEngine::selectThread(int index) +void GdbEngine::selectThread(ThreadId threadId) { - threadsHandler()->setCurrentThread(index); - Threads threads = threadsHandler()->threads(); - QTC_ASSERT(index < threads.size(), return); - const int id = threads.at(index).id; + threadsHandler()->setCurrentThread(threadId); showStatusMessage(tr("Retrieving data for stack view thread 0x%1...") - .arg(id, 0, 16), 10000); - postCommand("-thread-select " + QByteArray::number(id), Discardable, + .arg(threadId.raw(), 0, 16), 10000); + postCommand("-thread-select " + QByteArray::number(threadId.raw()), Discardable, CB(handleStackSelectThread)); } @@ -3640,17 +3700,21 @@ void GdbEngine::handleStackSelectFrame(const GdbResponse &response) void GdbEngine::handleThreadInfo(const GdbResponse &response) { if (response.resultClass == GdbResultDone) { - int currentThreadId; - const Threads threads = - ThreadsHandler::parseGdbmiThreads(response.data, ¤tThreadId); - threadsHandler()->setThreads(threads); - threadsHandler()->setCurrentThreadId(currentThreadId); + ThreadsHandler *handler = threadsHandler(); + handler->updateThreads(response.data); + // This is necessary as the current thread might not be in the list. + if (!handler->currentThread().isValid()) { + ThreadId other = handler->threadAt(0); + if (other.isValid()) + selectThread(other); + } updateViews(); // Adjust Threads combobox. if (m_hasInferiorThreadList && debuggerCore()->boolSetting(ShowThreadNames)) { postCommand("threadnames " + debuggerCore()->action(MaximalStackDepth)->value().toByteArray(), Discardable, CB(handleThreadNames)); } + reloadStack(false); // Will trigger register reload. } else { // Fall back for older versions: Try to get at least a list // of running threads. @@ -3662,37 +3726,29 @@ void GdbEngine::handleThreadListIds(const GdbResponse &response) { // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"} // In gdb 7.1+ additionally: current-thread-id="1" + ThreadsHandler *handler = threadsHandler(); const QList<GdbMi> items = response.data.findChild("thread-ids").children(); - Threads threads; for (int index = 0, n = items.size(); index != n; ++index) { ThreadData thread; - thread.id = items.at(index).data().toInt(); - threads.append(thread); + thread.id = ThreadId(items.at(index).data().toInt()); + handler->updateThread(thread); } - threadsHandler()->setThreads(threads); - threadsHandler()->setCurrentThreadId(m_currentThreadId); + reloadStack(false); // Will trigger register reload. } void GdbEngine::handleThreadNames(const GdbResponse &response) { if (response.resultClass == GdbResultDone) { + ThreadsHandler *handler = threadsHandler(); GdbMi names; names.fromString(response.consoleStreamOutput); - - Threads threads = threadsHandler()->threads(); - foreach (const GdbMi &name, names.children()) { - int id = name.findChild("id").data().toInt(); - for (int index = 0, n = threads.size(); index != n; ++index) { - ThreadData &thread = threads[index]; - if (thread.id == quint64(id)) { - thread.name = decodeData(name.findChild("value").data(), - name.findChild("valueencoded").data().toInt()); - break; - } - } + ThreadData thread; + thread.id = ThreadId(name.findChild("id").data().toInt()); + thread.name = decodeData(name.findChild("value").data(), + name.findChild("valueencoded").data().toInt()); + handler->updateThread(thread); } - threadsHandler()->setThreads(threads); updateViews(); } } @@ -4539,6 +4595,12 @@ DisassemblerLines GdbEngine::parseDisassembler(const GdbResponse &response) return parseCliDisassembler(response.consoleStreamOutput); } +void GdbEngine::reloadDisassembly() +{ + setTokenBarrier(); + updateLocals(); +} + void GdbEngine::handleDisassemblerCheck(const GdbResponse &response) { m_disassembleUsesComma = response.resultClass != GdbResultDone; @@ -4649,7 +4711,7 @@ void GdbEngine::startGdb(const QStringList &args) handleGdbStartFailed(); handleAdapterStartFailed( msgNoGdbBinaryForToolChain(sp.toolChainAbi), - _(Constants::DEBUGGER_COMMON_SETTINGS_ID)); + Constants::DEBUGGER_COMMON_SETTINGS_ID); return; } QStringList gdbArgs; @@ -4730,10 +4792,10 @@ void GdbEngine::startGdb(const QStringList &args) postCommand("set width 0"); postCommand("set height 0"); - postCommand("set breakpoint always-inserted on", ConsoleCommand); + //postCommand("set breakpoint always-inserted on", ConsoleCommand); // displaced-stepping does not work in Thumb mode. //postCommand("set displaced-stepping on"); - postCommand("set trust-readonly-sections on", ConsoleCommand); + //postCommand("set trust-readonly-sections on", ConsoleCommand); postCommand("set remotecache on", ConsoleCommand); //postCommand("set non-stop on", ConsoleCommand); @@ -4793,10 +4855,18 @@ void GdbEngine::startGdb(const QStringList &args) } else { m_fullStartDone = true; postCommand("set auto-solib-add on", ConsoleCommand); - loadPythonDumpers(); } + if (debuggerCore()->boolSetting(MultiInferior)) { + //postCommand("set follow-exec-mode new"); + postCommand("set detach-on-fork off"); + } + + // Quick check whether we have python. + postCommand("python print 43", ConsoleCommand, CB(handleHasPython)); + // Dummy command to guarantee a roundtrip before the adapter proceed. + // Make sure this stays the last command in startGdb(). postCommand("pwd", ConsoleCommand, CB(reportEngineSetupOk)); } @@ -4834,10 +4904,15 @@ void GdbEngine::loadInitScript() } } -void GdbEngine::loadPythonDumpers() +void GdbEngine::tryLoadPythonDumpers() { if (m_forceAsyncModel) return; + if (!m_hasPython) + return; + if (m_pythonAttemptedToLoad) + return; + m_pythonAttemptedToLoad = true; const QByteArray dumperSourcePath = Core::ICore::resourcePath().toLocal8Bit() + "/dumper/"; @@ -4849,9 +4924,22 @@ void GdbEngine::loadPythonDumpers() postCommand("python execfile('" + dumperSourcePath + "qttypes.py')", ConsoleCommand|NonCriticalResponse); + postCommand("python qqStringCutOff = " + + debuggerCore()->action(MaximalStringLength)->value().toByteArray(), + ConsoleCommand|NonCriticalResponse); + loadInitScript(); - postCommand("bbsetup", ConsoleCommand, CB(handleHasPython)); + postCommand("bbsetup", ConsoleCommand, CB(handlePythonSetup)); +} + +void GdbEngine::reloadDebuggingHelpers() +{ + // Only supported for python. + if (m_hasPython) { + m_pythonAttemptedToLoad = false; + tryLoadPythonDumpers(); + } } void GdbEngine::handleGdbError(QProcess::ProcessError error) @@ -4921,17 +5009,17 @@ void GdbEngine::abortDebugger() } void GdbEngine::handleAdapterStartFailed(const QString &msg, - const QString &settingsIdHint) + Core::Id settingsIdHint) { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); showMessage(_("ADAPTER START FAILED")); if (!msg.isEmpty()) { const QString title = tr("Adapter start failed"); - if (settingsIdHint.isEmpty()) { + if (!settingsIdHint.isValid()) { Core::ICore::showWarningWithOptions(title, msg); } else { Core::ICore::showWarningWithOptions(title, msg, QString(), - _(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint); + Constants::DEBUGGER_SETTINGS_CATEGORY, settingsIdHint); } } notifyEngineSetupFailed(); @@ -4951,6 +5039,11 @@ void GdbEngine::handleInferiorPrepared() QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); + if (debuggerCore()->boolSetting(IntelFlavor)) { + //postCommand("set follow-exec-mode new"); + postCommand("set disassembly-flavor intel"); + } + if (sp.breakOnMain) { QByteArray cmd = "tbreak "; cmd += sp.toolChainAbi.os() == Abi::WindowsOS ? "qMain" : "main"; @@ -4988,19 +5081,17 @@ void GdbEngine::finishInferiorSetup() void GdbEngine::handleDebugInfoLocation(const GdbResponse &response) { -#ifdef Q_OS_WIN - const char pathSep = ';'; -#else - const char pathSep = ':'; -#endif if (response.resultClass == GdbResultDone) { const QByteArray debugInfoLocation = startParameters().debugInfoLocation.toLocal8Bit(); if (QFile::exists(QString::fromLocal8Bit(debugInfoLocation))) { const QByteArray curDebugInfoLocations = response.consoleStreamOutput.split('"').value(1); - if (curDebugInfoLocations.isEmpty()) + if (curDebugInfoLocations.isEmpty()) { postCommand("set debug-file-directory " + debugInfoLocation); - else - postCommand("set debug-file-directory " + debugInfoLocation + pathSep + curDebugInfoLocations); + } else { + postCommand("set debug-file-directory " + debugInfoLocation + + HostOsInfo::pathListSeparator().toLatin1() + + curDebugInfoLocations); + } } } } @@ -5033,13 +5124,18 @@ void GdbEngine::handleNamespaceExtraction(const GdbResponse &response) } else { if (debuggerCore()->boolSetting(BreakOnAbort)) postCommand("-break-insert -f abort"); - if (debuggerCore()->boolSetting(BreakOnWarning)) + if (debuggerCore()->boolSetting(BreakOnWarning)) { postCommand("-break-insert -f '" + qtNamespace() + "qWarning'"); - if (debuggerCore()->boolSetting(BreakOnFatal)) + postCommand("-break-insert -f '" + qtNamespace() + "QMessageLogger::warning'"); + } + if (debuggerCore()->boolSetting(BreakOnFatal)) { postCommand("-break-insert -f '" + qtNamespace() + "qFatal'", - CB(handleBreakOnQFatal)); - else + CB(handleBreakOnQFatal), QVariant(false)); + postCommand("-break-insert -f '" + qtNamespace() + "QMessageLogger::fatal'", + CB(handleBreakOnQFatal), QVariant(true)); + } else { notifyInferiorSetupOk(); + } } } @@ -5056,7 +5152,8 @@ void GdbEngine::handleBreakOnQFatal(const GdbResponse &response) } // Continue setup. - notifyInferiorSetupOk(); + if (response.cookie.toBool()) + notifyInferiorSetupOk(); } void GdbEngine::notifyInferiorSetupFailed(const QString &msg) @@ -5309,7 +5406,7 @@ bool GdbEngine::prepareCommand() // perr == BadQuoting is never returned on Windows // FIXME? QTCREATORBUG-2809 handleAdapterStartFailed(QCoreApplication::translate("DebuggerEngine", // Same message in CdbEngine - "Debugging complex command lines is currently not supported on Windows."), QString()); + "Debugging complex command lines is currently not supported on Windows."), Core::Id()); return false; } #endif @@ -5362,6 +5459,14 @@ void GdbEngine::interruptLocalInferior(qint64 pid) } } +QByteArray GdbEngine::dotEscape(QByteArray str) +{ + str.replace(' ', '.'); + str.replace('\\', '.'); + str.replace('/', '.'); + return str; +} + // // Factory // diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 689c133e00..b70d9e4756 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -35,6 +35,9 @@ #include "stackframe.h" #include "watchhandler.h" #include "watchutils.h" +#include "threaddata.h" + +#include <coreplugin/id.h> #include <QByteArray> #include <QProcess> @@ -227,18 +230,19 @@ protected: ////////// Gdb Process Management ////////// void startGdb(const QStringList &args = QStringList()); void reportEngineSetupOk(const GdbResponse &response); + void handleCheckForPython(const GdbResponse &response); void handleInferiorShutdown(const GdbResponse &response); void handleGdbExit(const GdbResponse &response); void handleNamespaceExtraction(const GdbResponse &response); void loadInitScript(); - void loadPythonDumpers(); + void tryLoadPythonDumpers(); void pythonDumpersFailed(); // Something went wrong with the adapter *before* adapterStarted() was emitted. // Make sure to clean up everything before emitting this signal. void handleAdapterStartFailed(const QString &msg, - const QString &settingsIdHint = QString()); + Core::Id settingsIdHint = Core::Id()); // This triggers the initial breakpoint synchronization and causes // finishInferiorSetup() being called once done. @@ -387,6 +391,7 @@ protected: void handleShowVersion(const GdbResponse &response); void handleListFeatures(const GdbResponse &response); void handleHasPython(const GdbResponse &response); + void handlePythonSetup(const GdbResponse &response); int m_gdbVersion; // 6.8.0 is 60800 int m_gdbBuildVersion; // MAC only? @@ -437,7 +442,7 @@ private: ////////// Inferior Management ////////// private: ////////// View & Data Stuff ////////// protected: - void selectThread(int index); + void selectThread(ThreadId threadId); void activateFrame(int index); void resetLocation(); @@ -475,11 +480,13 @@ private: ////////// View & Data Stuff ////////// Q_SLOT void loadAllSymbols(); void loadSymbolsForStack(); void requestModuleSymbols(const QString &moduleName); + void requestModuleSections(const QString &moduleName); void reloadModules(); void examineModules(); void reloadModulesInternal(); void handleModulesList(const GdbResponse &response); void handleShowModuleSymbols(const GdbResponse &response); + void handleShowModuleSections(const GdbResponse &response); // // Snapshot specific stuff @@ -523,6 +530,7 @@ private: ////////// View & Data Stuff ////////// DisassemblerLines parseDisassembler(const GdbResponse &response); DisassemblerLines parseCliDisassembler(const QByteArray &response); DisassemblerLines parseMiDisassembler(const GdbMi &response); + Q_SLOT void reloadDisassembly(); bool m_disassembleUsesComma; @@ -645,6 +653,7 @@ protected: bool checkDebuggingHelpersClassic(); void setDebuggingHelperStateClassic(DebuggingHelperState); void tryLoadDebuggingHelpersClassic(); + void reloadDebuggingHelpers(); DebuggingHelperState m_debuggingHelperState; DumperHelper m_dumperHelper; @@ -661,10 +670,8 @@ protected: QString tooltipExpression() const; QScopedPointer<GdbToolTipContext> m_toolTipContext; - // For short-circuiting stack and thread list evaluation. bool m_stackNeeded; - int m_currentThreadId; // // Qml @@ -701,6 +708,7 @@ protected: // debug information. bool attemptQuickStart() const; bool m_fullStartDone; + bool m_pythonAttemptedToLoad; // Test bool m_forceAsyncModel; @@ -713,6 +721,7 @@ protected: static QString msgInferiorSetupOk(); static QString msgInferiorRunOk(); static QString msgConnectRemoteServerFailed(const QString &why); + static QByteArray dotEscape(QByteArray str); protected: enum DumperHandling diff --git a/src/plugins/debugger/gdb/gdbmi.cpp b/src/plugins/debugger/gdb/gdbmi.cpp deleted file mode 100644 index 609b50a874..0000000000 --- a/src/plugins/debugger/gdb/gdbmi.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "gdbmi.h" - -#include <utils/qtcassert.h> - -#include <QByteArray> -#include <QDebug> -#include <QRegExp> -#include <QTextStream> - -#include <ctype.h> - -namespace Debugger { -namespace Internal { - -void skipCommas(const char *&from, const char *to) -{ - while (*from == ',' && from != to) - ++from; -} - -QTextStream &operator<<(QTextStream &os, const GdbMi &mi) -{ - return os << mi.toString(); -} - -void GdbMi::parseResultOrValue(const char *&from, const char *to) -{ - while (from != to && isspace(*from)) - ++from; - - //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from); - parseValue(from, to); - if (isValid()) { - //qDebug() << "no valid result in " << QByteArray(from, to - from); - return; - } - if (from == to || *from == '(') - return; - const char *ptr = from; - while (ptr < to && *ptr != '=') { - //qDebug() << "adding" << QChar(*ptr) << "to name"; - ++ptr; - } - m_name = QByteArray(from, ptr - from); - from = ptr; - if (from < to && *from == '=') { - ++from; - parseValue(from, to); - } -} - -QByteArray GdbMi::parseCString(const char *&from, const char *to) -{ - QByteArray result; - //qDebug() << "parseCString: " << QByteArray(from, to - from); - if (*from != '"') { - qDebug() << "MI Parse Error, double quote expected"; - ++from; // So we don't hang - return QByteArray(); - } - const char *ptr = from; - ++ptr; - while (ptr < to) { - if (*ptr == '"') { - ++ptr; - result = QByteArray(from + 1, ptr - from - 2); - break; - } - if (*ptr == '\\') { - ++ptr; - if (ptr == to) { - qDebug() << "MI Parse Error, unterminated backslash escape"; - from = ptr; // So we don't hang - return QByteArray(); - } - } - ++ptr; - } - from = ptr; - - int idx = result.indexOf('\\'); - if (idx >= 0) { - char *dst = result.data() + idx; - const char *src = dst + 1, *end = result.data() + result.length(); - do { - char c = *src++; - switch (c) { - case 'a': *dst++ = '\a'; break; - case 'b': *dst++ = '\b'; break; - case 'f': *dst++ = '\f'; break; - case 'n': *dst++ = '\n'; break; - case 'r': *dst++ = '\r'; break; - case 't': *dst++ = '\t'; break; - case 'v': *dst++ = '\v'; break; - case '"': *dst++ = '"'; break; - case '\\': *dst++ = '\\'; break; - default: - { - int chars = 0; - uchar prod = 0; - forever { - if (c < '0' || c > '7') { - --src; - break; - } - prod = prod * 8 + c - '0'; - if (++chars == 3 || src == end) - break; - c = *src++; - } - if (!chars) { - qDebug() << "MI Parse Error, unrecognized backslash escape"; - return QByteArray(); - } - *dst++ = prod; - } - } - while (src != end) { - char c = *src++; - if (c == '\\') - break; - *dst++ = c; - } - } while (src != end); - *dst = 0; - result.truncate(dst - result.data()); - } - - return result; -} - -void GdbMi::parseValue(const char *&from, const char *to) -{ - //qDebug() << "parseValue: " << QByteArray(from, to - from); - switch (*from) { - case '{': - parseTuple(from, to); - break; - case '[': - parseList(from, to); - break; - case '"': - m_type = Const; - m_data = parseCString(from, to); - break; - default: - break; - } -} - - -void GdbMi::parseTuple(const char *&from, const char *to) -{ - //qDebug() << "parseTuple: " << QByteArray(from, to - from); - QTC_CHECK(*from == '{'); - ++from; - parseTuple_helper(from, to); -} - -void GdbMi::parseTuple_helper(const char *&from, const char *to) -{ - skipCommas(from, to); - //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from); - m_type = Tuple; - while (from < to) { - if (*from == '}') { - ++from; - break; - } - GdbMi child; - child.parseResultOrValue(from, to); - //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n"; - if (!child.isValid()) - return; - m_children += child; - skipCommas(from, to); - } -} - -void GdbMi::parseList(const char *&from, const char *to) -{ - //qDebug() << "parseList: " << QByteArray(from, to - from); - QTC_CHECK(*from == '['); - ++from; - m_type = List; - skipCommas(from, to); - while (from < to) { - if (*from == ']') { - ++from; - break; - } - GdbMi child; - child.parseResultOrValue(from, to); - if (child.isValid()) - m_children += child; - skipCommas(from, to); - } -} - -static QByteArray ind(int indent) -{ - return QByteArray(2 * indent, ' '); -} - -void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const -{ - for (int i = 0; i < m_children.size(); ++i) { - if (i != 0) { - *str += ','; - if (multiline) - *str += '\n'; - } - if (multiline) - *str += ind(indent); - *str += m_children.at(i).toString(multiline, indent); - } -} - -QByteArray GdbMi::escapeCString(const QByteArray &ba) -{ - QByteArray ret; - ret.reserve(ba.length() * 2); - for (int i = 0; i < ba.length(); ++i) { - const uchar c = ba.at(i); - switch (c) { - case '\\': ret += "\\\\"; break; - case '\a': ret += "\\a"; break; - case '\b': ret += "\\b"; break; - case '\f': ret += "\\f"; break; - case '\n': ret += "\\n"; break; - case '\r': ret += "\\r"; break; - case '\t': ret += "\\t"; break; - case '\v': ret += "\\v"; break; - case '"': ret += "\\\""; break; - default: - if (c < 32 || c == 127) { - ret += '\\'; - ret += ('0' + (c >> 6)); - ret += ('0' + ((c >> 3) & 7)); - ret += ('0' + (c & 7)); - } else { - ret += c; - } - } - } - return ret; -} - -QByteArray GdbMi::toString(bool multiline, int indent) const -{ - QByteArray result; - switch (m_type) { - case Invalid: - if (multiline) - result += ind(indent) + "Invalid\n"; - else - result += "Invalid"; - break; - case Const: - if (!m_name.isEmpty()) - result += m_name + '='; - result += '"' + escapeCString(m_data) + '"'; - break; - case Tuple: - if (!m_name.isEmpty()) - result += m_name + '='; - if (multiline) { - result += "{\n"; - dumpChildren(&result, multiline, indent + 1); - result += '\n' + ind(indent) + '}'; - } else { - result += '{'; - dumpChildren(&result, multiline, indent + 1); - result += '}'; - } - break; - case List: - if (!m_name.isEmpty()) - result += m_name + '='; - if (multiline) { - result += "[\n"; - dumpChildren(&result, multiline, indent + 1); - result += '\n' + ind(indent) + ']'; - } else { - result += '['; - dumpChildren(&result, multiline, indent + 1); - result += ']'; - } - break; - } - return result; -} - -void GdbMi::fromString(const QByteArray &ba) -{ - const char *from = ba.constBegin(); - const char *to = ba.constEnd(); - parseResultOrValue(from, to); -} - -void GdbMi::fromStringMultiple(const QByteArray &ba) -{ - const char *from = ba.constBegin(); - const char *to = ba.constEnd(); - parseTuple_helper(from, to); -} - -GdbMi GdbMi::findChild(const char *name) const -{ - for (int i = 0; i < m_children.size(); ++i) - if (m_children.at(i).m_name == name) - return m_children.at(i); - return GdbMi(); -} - -qulonglong GdbMi::toAddress() const -{ - QByteArray ba = m_data; - if (ba.endsWith('L')) - ba.chop(1); - if (ba.startsWith('*') || ba.startsWith('@')) - ba = ba.mid(1); - return ba.toULongLong(0, 0); -} - -////////////////////////////////////////////////////////////////////////////////// -// -// GdbResponse -// -////////////////////////////////////////////////////////////////////////////////// - -QByteArray GdbResponse::stringFromResultClass(GdbResultClass resultClass) -{ - switch (resultClass) { - case GdbResultDone: return "done"; - case GdbResultRunning: return "running"; - case GdbResultConnected: return "connected"; - case GdbResultError: return "error"; - case GdbResultExit: return "exit"; - default: return "unknown"; - } -} - -QByteArray GdbResponse::toString() const -{ - QByteArray result; - if (token != -1) - result = QByteArray::number(token); - result += '^'; - result += stringFromResultClass(resultClass); - if (data.isValid()) - result += ',' + data.toString(); - result += '\n'; - return result; -} - - -////////////////////////////////////////////////////////////////////////////////// -// -// GdbResponse -// -////////////////////////////////////////////////////////////////////////////////// - -void extractGdbVersion(const QString &msg, - int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb) -{ - const QChar dot(QLatin1Char('.')); - - QString cleaned; - QString build; - bool inClean = true; - foreach (QChar c, msg) { - if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace())) - inClean = false; - if (inClean) { - if (c.isDigit()) - cleaned.append(c); - else if (!cleaned.isEmpty() && !cleaned.endsWith(dot)) - cleaned.append(dot); - } else { - if (c.isDigit()) - build.append(c); - else if (!build.isEmpty() && !build.endsWith(dot)) - build.append(dot); - } - } - - *isMacGdb = msg.contains(QLatin1String("Apple version")); - *isQnxGdb = msg.contains(QLatin1String("qnx")); - - *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt() - + 100 * cleaned.section(dot, 1, 1).toInt() - + 1 * cleaned.section(dot, 2, 2).toInt(); - if (cleaned.count(dot) >= 3) - *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt(); - else - *gdbBuildVersion = build.section(dot, 0, 0).toInt(); - - if (*isMacGdb) - *gdbBuildVersion = build.section(dot, 1, 1).toInt(); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/gdbmi.h b/src/plugins/debugger/gdb/gdbmi.h deleted file mode 100644 index b9d3e39ae3..0000000000 --- a/src/plugins/debugger/gdb/gdbmi.h +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef DEBUGGER_GDBMI_H -#define DEBUGGER_GDBMI_H - -#include <QByteArray> -#include <QList> -#include <QVariant> - -namespace Debugger { -namespace Internal { - -/* - -output ==> - ( out-of-band-record )* [ result-record ] "(gdb)" nl -result-record ==> - [ token ] "^" result-class ( "," result )* nl -out-of-band-record ==> - async-record | stream-record -async-record ==> - exec-async-output | status-async-output | notify-async-output -exec-async-output ==> - [ token ] "*" async-output -status-async-output ==> - [ token ] "+" async-output -notify-async-output ==> - [ token ] "=" async-output -async-output ==> - async-class ( "," result )* nl -result-class ==> - "done" | "running" | "connected" | "error" | "exit" -async-class ==> - "stopped" | others (where others will be added depending on the needs--this is still in development). -result ==> - variable "=" value -variable ==> - string -value ==> - const | tuple | list -const ==> - c-string -tuple ==> - "{}" | "{" result ( "," result )* "}" -list ==> - "[]" | "[" value ( "," value )* "]" | "[" result ( "," result )* "]" -stream-record ==> - console-stream-output | target-stream-output | log-stream-output -console-stream-output ==> - "~" c-string -target-stream-output ==> - "@" c-string -log-stream-output ==> - "&" c-string -nl ==> - CR | CR-LF -token ==> - any sequence of digits. - - */ - -// FIXME: rename into GdbMiValue -class GdbMi -{ -public: - GdbMi() : m_type(Invalid) {} - - QByteArray m_name; - QByteArray m_data; - QList<GdbMi> m_children; - - enum Type { - Invalid, - Const, - Tuple, - List - }; - - Type m_type; - - inline Type type() const { return m_type; } - inline QByteArray name() const { return m_name; } - inline bool hasName(const char *name) const { return m_name == name; } - - inline bool isValid() const { return m_type != Invalid; } - inline bool isConst() const { return m_type == Const; } - inline bool isTuple() const { return m_type == Tuple; } - inline bool isList() const { return m_type == List; } - - - inline QByteArray data() const { return m_data; } - inline const QList<GdbMi> &children() const { return m_children; } - inline int childCount() const { return m_children.size(); } - - const GdbMi &childAt(int index) const { return m_children[index]; } - GdbMi &childAt(int index) { return m_children[index]; } - GdbMi findChild(const char *name) const; - - QByteArray toString(bool multiline = false, int indent = 0) const; - qulonglong toAddress() const; - void fromString(const QByteArray &str); - void fromStringMultiple(const QByteArray &str); - -private: - friend class GdbResponse; - friend class GdbEngine; - - static QByteArray parseCString(const char *&from, const char *to); - static QByteArray escapeCString(const QByteArray &ba); - void parseResultOrValue(const char *&from, const char *to); - void parseValue(const char *&from, const char *to); - void parseTuple(const char *&from, const char *to); - void parseTuple_helper(const char *&from, const char *to); - void parseList(const char *&from, const char *to); - - void dumpChildren(QByteArray *str, bool multiline, int indent) const; -}; - -enum GdbResultClass -{ - // "done" | "running" | "connected" | "error" | "exit" - GdbResultUnknown, - GdbResultDone, - GdbResultRunning, - GdbResultConnected, - GdbResultError, - GdbResultExit -}; - -class GdbResponse -{ -public: - GdbResponse() : token(-1), resultClass(GdbResultUnknown) {} - QByteArray toString() const; - static QByteArray stringFromResultClass(GdbResultClass resultClass); - - int token; - GdbResultClass resultClass; - GdbMi data; - QVariant cookie; - QByteArray logStreamOutput; - QByteArray consoleStreamOutput; -}; - -void extractGdbVersion(const QString &msg, - int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb); - -} // namespace Internal -} // namespace Debugger - -//Q_DECLARE_METATYPE(GdbDebugger::Internal::GdbMi) - -#endif // DEBUGGER_GDBMI_H diff --git a/src/plugins/debugger/gdb/gdboptionspage.cpp b/src/plugins/debugger/gdb/gdboptionspage.cpp index 0d023f1c09..014db0cf01 100644 --- a/src/plugins/debugger/gdb/gdboptionspage.cpp +++ b/src/plugins/debugger/gdb/gdboptionspage.cpp @@ -76,9 +76,13 @@ public: QCheckBox *checkBoxBreakOnAbort; QCheckBox *checkBoxEnableReverseDebugging; QCheckBox *checkBoxAttemptQuickStart; + QCheckBox *checkBoxMultiInferior; + QCheckBox *checkBoxIntelFlavor; QGroupBox *groupBoxStartupCommands; QTextEdit *textEditStartupCommands; + QGroupBox *groupBoxPostAttachCommands; + QTextEdit *textEditPostAttachCommands; //QGroupBox *groupBoxPluginDebugging; //QRadioButton *radioButtonAllPluginBreakpoints; @@ -183,6 +187,7 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) "of debug information such as <i>/usr/src/debug</i> " "when starting GDB.</body></html>")); + // #fixme: 2.7 Move to common settings page. checkBoxBreakOnWarning = new QCheckBox(groupBoxGeneral); checkBoxBreakOnWarning->setText(CommonOptionsPage::msgSetBreakpointAtFunction("qWarning")); checkBoxBreakOnWarning->setToolTip(CommonOptionsPage::msgSetBreakpointAtFunctionToolTip("qWarning")); @@ -210,13 +215,30 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) "This can result in faster startup times at the price of not being able to " "set breakpoints by file and number.</body></html>")); + checkBoxMultiInferior = new QCheckBox(groupBoxGeneral); + checkBoxMultiInferior->setText(GdbOptionsPage::tr("Debug all children")); + checkBoxMultiInferior->setToolTip(GdbOptionsPage::tr( + "<html><head/><body>Keep debugging all children after a fork." + "</body></html>")); + + checkBoxIntelFlavor = new QCheckBox(groupBoxGeneral); + checkBoxIntelFlavor->setText(GdbOptionsPage::tr("Use Intel style disassembly")); + checkBoxIntelFlavor->setToolTip(GdbOptionsPage::tr( + "<html><head/><body>GDB shows by default AT&&T style disassembly." + "</body></html>")); + groupBoxStartupCommands = new QGroupBox(this); groupBoxStartupCommands->setTitle(GdbOptionsPage::tr("Additional Startup Commands")); groupBoxStartupCommands->setToolTip(GdbOptionsPage::tr( "<html><head/><body><p>GDB commands entered here will be executed after " "GDB has been started and the debugging helpers have been initialized.</p>" "<p>You can add commands to load further debugging helpers here, or " - "modify existing ones.</p><p>To execute arbitrary Python scripts, " + "modify existing ones.</p>" + "<p>To execute simple Python commands, prefix them with \"python\".</p>" + "<p>To execute sequences of Python commands spanning multiple lines " + "prepend the block with \"python\" on a separate line, and append " + "\"end\" on a separate line.</p>" + "<p>To execute arbitrary Python scripts, " "use <i>python execfile('/path/to/script.py')</i>.</p>" "</body></html>")); @@ -224,6 +246,19 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) textEditStartupCommands->setAcceptRichText(false); textEditStartupCommands->setToolTip(groupBoxStartupCommands->toolTip()); + groupBoxPostAttachCommands = new QGroupBox(this); + groupBoxPostAttachCommands->setTitle(GdbOptionsPage::tr("Additional Attach Commands")); + groupBoxPostAttachCommands->setToolTip(GdbOptionsPage::tr( + "<html><head/><body><p>GDB commands entered here will be executed after " + "GDB has successfully attached to remote targets.</p>" + "<p>You can add commands to further set up the target here, " + "such as \"monitor reset\" or \"load\"." + "</body></html>")); + + textEditPostAttachCommands = new QTextEdit(groupBoxPostAttachCommands); + textEditPostAttachCommands->setAcceptRichText(false); + textEditPostAttachCommands->setToolTip(groupBoxPostAttachCommands->toolTip()); + /* groupBoxPluginDebugging = new QGroupBox(q); groupBoxPluginDebugging->setTitle(GdbOptionsPage::tr( @@ -258,6 +293,7 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) formLayout->addRow(checkBoxUseDynamicType); formLayout->addRow(checkBoxLoadGdbInit); formLayout->addRow(checkBoxWarnOnReleaseBuilds); + formLayout->addRow(checkBoxIntelFlavor); formLayout->addRow(labelDangerous); formLayout->addRow(checkBoxTargetAsync); formLayout->addRow(checkBoxAutoEnrichParameters); @@ -266,18 +302,23 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) formLayout->addRow(checkBoxBreakOnAbort); formLayout->addRow(checkBoxEnableReverseDebugging); formLayout->addRow(checkBoxAttemptQuickStart); + formLayout->addRow(checkBoxMultiInferior); QGridLayout *startLayout = new QGridLayout(groupBoxStartupCommands); startLayout->addWidget(textEditStartupCommands, 0, 0, 1, 1); + QGridLayout *postAttachLayout = new QGridLayout(groupBoxPostAttachCommands); + postAttachLayout->addWidget(textEditPostAttachCommands, 0, 0, 1, 1); + //QHBoxLayout *horizontalLayout = new QHBoxLayout(); //horizontalLayout->addItem(new QSpacerItem(10, 10, QSizePolicy::Preferred, QSizePolicy::Minimum)); //horizontalLayout->addWidget(labelSelectedPluginBreakpoints); //horizontalLayout->addWidget(lineEditSelectedPluginBreakpointsPattern); QGridLayout *gridLayout = new QGridLayout(this); - gridLayout->addWidget(groupBoxGeneral, 0, 0); - gridLayout->addWidget(groupBoxStartupCommands, 0, 1); + gridLayout->addWidget(groupBoxGeneral, 0, 0, 2, 1); + gridLayout->addWidget(groupBoxStartupCommands, 0, 1, 1, 1); + gridLayout->addWidget(groupBoxPostAttachCommands, 1, 1, 1, 1); //gridLayout->addWidget(groupBoxStartupCommands, 0, 1, 1, 1); //gridLayout->addWidget(radioButtonAllPluginBreakpoints, 0, 0, 1, 1); @@ -289,6 +330,7 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) DebuggerCore *dc = debuggerCore(); group.insert(dc->action(GdbStartupCommands), textEditStartupCommands); + group.insert(dc->action(GdbPostAttachCommands), textEditPostAttachCommands); group.insert(dc->action(LoadGdbInit), checkBoxLoadGdbInit); group.insert(dc->action(AutoEnrichParameters), checkBoxAutoEnrichParameters); group.insert(dc->action(UseDynamicType), checkBoxUseDynamicType); @@ -300,6 +342,8 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) group.insert(dc->action(BreakOnAbort), checkBoxBreakOnAbort); group.insert(dc->action(GdbWatchdogTimeout), spinBoxGdbWatchdogTimeout); group.insert(dc->action(AttemptQuickStart), checkBoxAttemptQuickStart); + group.insert(dc->action(MultiInferior), checkBoxMultiInferior); + group.insert(dc->action(IntelFlavor), checkBoxIntelFlavor); group.insert(dc->action(UseMessageBoxForSignals), checkBoxUseMessageBoxForSignals); group.insert(dc->action(SkipKnownFrames), checkBoxSkipKnownFrames); @@ -324,6 +368,7 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) << sep << checkBoxUseMessageBoxForSignals->text() << sep << checkBoxAdjustBreakpointLocations->text() << sep << checkBoxAttemptQuickStart->text() + << sep << checkBoxMultiInferior->text() // << sep << groupBoxPluginDebugging->title() // << sep << radioButtonAllPluginBreakpoints->text() // << sep << radioButtonSelectedPluginBreakpoints->text() @@ -335,9 +380,9 @@ GdbOptionsPageWidget::GdbOptionsPageWidget(QWidget *parent) GdbOptionsPage::GdbOptionsPage() { - setId(QLatin1String("M.Gdb")); + setId("M.Gdb"); setDisplayName(tr("GDB")); - setCategory(QLatin1String(Constants::DEBUGGER_SETTINGS_CATEGORY)); + setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY); setDisplayCategory(QCoreApplication::translate("Debugger", Constants::DEBUGGER_SETTINGS_TR_CATEGORY)); setCategoryIcon(QLatin1String(Constants::DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON)); } diff --git a/src/plugins/debugger/gdb/localplaingdbadapter.cpp b/src/plugins/debugger/gdb/localplaingdbadapter.cpp index 5d5ce71407..366b20860a 100644 --- a/src/plugins/debugger/gdb/localplaingdbadapter.cpp +++ b/src/plugins/debugger/gdb/localplaingdbadapter.cpp @@ -37,6 +37,7 @@ #include <projectexplorer/abi.h> +#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <QFileInfo> @@ -61,11 +62,8 @@ GdbLocalPlainEngine::GdbLocalPlainEngine(const DebuggerStartParameters &startPar GdbEngine::DumperHandling GdbLocalPlainEngine::dumperHandling() const { // LD_PRELOAD fails for System-Qt on Mac. -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - return DumperLoadedByGdb; -#else - return DumperLoadedByGdbPreload; -#endif + return Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost() + ? DumperLoadedByGdb : DumperLoadedByGdbPreload; } void GdbLocalPlainEngine::setupEngine() diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp index 623c0318da..9b8ed82ea4 100644 --- a/src/plugins/debugger/gdb/pythongdbengine.cpp +++ b/src/plugins/debugger/gdb/pythongdbengine.cpp @@ -28,7 +28,8 @@ ****************************************************************************/ #include "gdbengine.h" -#include "gdbmi.h" + +#include "debuggerprotocol.h" #include "debuggeractions.h" #include "debuggercore.h" #include "debuggerstringutils.h" diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp index f9a583003f..733e7b5a9e 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp @@ -30,15 +30,16 @@ #include "remotegdbserveradapter.h" #include "debuggeractions.h" -#include "debuggerstartparameters.h" #include "debuggercore.h" +#include "debuggerprotocol.h" +#include "debuggerstartparameters.h" #include "debuggerstringutils.h" -#include "gdbengine.h" -#include "gdbmi.h" -#include <utils/qtcassert.h> -#include <utils/fancymainwindow.h> #include <projectexplorer/abi.h> +#include <utils/fancymainwindow.h> +#include <utils/hostosinfo.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <QFileInfo> #include <QMessageBox> @@ -84,7 +85,14 @@ void GdbRemoteServerEngine::setupEngine() QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); showMessage(_("TRYING TO START ADAPTER")); if (!startParameters().serverStartScript.isEmpty()) { - m_uploadProc.start(_("/bin/sh ") + startParameters().serverStartScript); + + // Provide script information about the environment + QString arglist; + Utils::QtcProcess::addArg(&arglist, startParameters().serverStartScript); + Utils::QtcProcess::addArg(&arglist, startParameters().executable); + Utils::QtcProcess::addArg(&arglist, startParameters().remoteChannel); + + m_uploadProc.start(_("/bin/sh ") + arglist); m_uploadProc.waitForStarted(); } if (startParameters().remoteSetupNeeded) @@ -156,11 +164,6 @@ void GdbRemoteServerEngine::setupInferior() { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); const DebuggerStartParameters &sp = startParameters(); -#ifdef Q_OS_WIN - #define PATHSEP ";" -#else - #define PATHSEP ":" -#endif QString executableFileName; if (!sp.executable.isEmpty()) { QFileInfo fi(sp.executable); @@ -178,7 +181,8 @@ void GdbRemoteServerEngine::setupInferior() // if (!remoteArch.isEmpty()) // postCommand("set architecture " + remoteArch); - const QString solibSearchPath = sp.solibSearchPath.join(QLatin1String(PATHSEP)); + const QString solibSearchPath + = sp.solibSearchPath.join(QString(Utils::HostOsInfo::pathListSeparator())); if (!solibSearchPath.isEmpty()) postCommand("set solib-search-path " + solibSearchPath.toLocal8Bit()); @@ -285,6 +289,11 @@ void GdbRemoteServerEngine::handleTargetRemote(const GdbResponse &record) // gdb server will stop the remote application itself. showMessage(_("INFERIOR STARTED")); showMessage(msgAttachedToStoppedInferior(), StatusBar); + QString postAttachCommands = debuggerCore()->stringSetting(GdbPostAttachCommands); + if (!postAttachCommands.isEmpty()) { + foreach (const QString &cmd, postAttachCommands.split(QLatin1Char('\n'))) + postCommand(cmd.toLatin1()); + } handleInferiorPrepared(); } else { // 16^error,msg="hd:5555: Connection timed out." @@ -304,11 +313,10 @@ void GdbRemoteServerEngine::handleTargetQnx(const GdbResponse &response) showMessage(msgAttachedToStoppedInferior(), StatusBar); const qint64 pid = isMasterEngine() ? startParameters().attachPID : masterEngine()->startParameters().attachPID; - if (pid > -1) { + if (pid > -1) postCommand("attach " + QByteArray::number(pid), CB(handleAttach)); - } else { + else handleInferiorPrepared(); - } } else { // 16^error,msg="hd:5555: Connection timed out." QString msg = msgConnectRemoteServerFailed( diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp index 8ede54051a..d0f19cd2a6 100644 --- a/src/plugins/debugger/gdb/termgdbadapter.cpp +++ b/src/plugins/debugger/gdb/termgdbadapter.cpp @@ -29,14 +29,14 @@ #include "termgdbadapter.h" +#include "debuggercore.h" +#include "debuggerprotocol.h" #include "debuggerstartparameters.h" -#include "gdbmi.h" -#include "gdbengine.h" -#include "procinterrupt.h" #include "debuggerstringutils.h" -#include "debuggercore.h" +#include "procinterrupt.h" #include "shared/hostutils.h" +#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <coreplugin/icore.h> @@ -60,11 +60,10 @@ GdbTermEngine::GdbTermEngine(const DebuggerStartParameters &startParameters) { #ifdef Q_OS_WIN // Windows up to xp needs a workaround for attaching to freshly started processes. see proc_stub_win - if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) { + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) m_stubProc.setMode(Utils::ConsoleProcess::Suspend); - } else { + else m_stubProc.setMode(Utils::ConsoleProcess::Debug); - } #else m_stubProc.setMode(Utils::ConsoleProcess::Debug); m_stubProc.setSettings(Core::ICore::settings()); @@ -79,11 +78,9 @@ GdbTermEngine::~GdbTermEngine() GdbEngine::DumperHandling GdbTermEngine::dumperHandling() const { // LD_PRELOAD fails for System-Qt on Mac. -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - return DumperLoadedByGdb; -#else - return DumperLoadedByAdapter; // Handles loading itself via LD_PRELOAD -#endif + return Utils::HostOsInfo::isWindowsHost() || Utils::HostOsInfo::isMacHost() + ? DumperLoadedByGdb + : DumperLoadedByAdapter; // Handles loading itself via LD_PRELOAD } void GdbTermEngine::setupEngine() @@ -106,7 +103,7 @@ void GdbTermEngine::setupEngine() connect(&m_stubProc, SIGNAL(processError(QString)), SLOT(stubError(QString))); connect(&m_stubProc, SIGNAL(processStarted()), SLOT(stubStarted())); - connect(&m_stubProc, SIGNAL(wrapperStopped()), SLOT(stubExited())); + connect(&m_stubProc, SIGNAL(stubStopped()), SLOT(stubExited())); // FIXME: Starting the stub implies starting the inferior. This is // fairly unclean as far as the state machine and error reporting go. @@ -146,25 +143,29 @@ void GdbTermEngine::setupInferior() void GdbTermEngine::handleStubAttached(const GdbResponse &response) { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); -#ifdef Q_OS_WIN - QString errorMessage; -#endif // Q_OS_WIN + switch (response.resultClass) { case GdbResultDone: case GdbResultRunning: -#ifdef Q_OS_WIN - // Resume thread that was suspended by console stub process (see stub code). - if (winResumeThread(m_stubProc.applicationMainThreadID(), &errorMessage)) { - showMessage(QString::fromLatin1("Inferior attached, thread %1 resumed"). - arg(m_stubProc.applicationMainThreadID()), LogMisc); + if (startParameters().toolChainAbi.os() != ProjectExplorer::Abi::WindowsOS) { + showMessage(_("INFERIOR ATTACHED")); } else { - showMessage(QString::fromLatin1("Inferior attached, unable to resume thread %1: %2"). - arg(m_stubProc.applicationMainThreadID()).arg(errorMessage), - LogWarning); - } + QString errorMessage; + // Resume thread that was suspended by console stub process (see stub code). +#ifdef Q_OS_WIN + const qint64 mainThreadId = m_stubProc.applicationMainThreadID(); #else - showMessage(_("INFERIOR ATTACHED")); -#endif // Q_OS_WIN + const qint64 mainThreadId = -1; +#endif + if (winResumeThread(mainThreadId, &errorMessage)) { + showMessage(QString::fromLatin1("Inferior attached, thread %1 resumed"). + arg(mainThreadId), LogMisc); + } else { + showMessage(QString::fromLatin1("Inferior attached, unable to resume thread %1: %2"). + arg(mainThreadId).arg(errorMessage), + LogWarning); + } + } handleInferiorPrepared(); break; case GdbResultError: |