/**************************************************************************** ** ** 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 "lldbengine.h" #include "debuggeractions.h" #include "debuggercore.h" #include "debuggerdialogs.h" #include "debuggerplugin.h" #include "debuggerprotocol.h" #include "debuggerstartparameters.h" #include "debuggerstringutils.h" #include "debuggertooltipmanager.h" #include "breakhandler.h" #include "moduleshandler.h" #include "registerhandler.h" #include "stackhandler.h" #include "sourceutils.h" #include "watchhandler.h" #include "watchutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_SCRIPT 1 #if DEBUG_SCRIPT # define SDEBUG(s) qDebug() << s #else # define SDEBUG(s) #endif # define XSDEBUG(s) qDebug() << s #define CB(callback) &LldbEngine::callback, STRINGIFY(callback) namespace Debugger { namespace Internal { static QByteArray quoteUnprintable(const QByteArray &ba) { QByteArray res; char buf[10]; for (int i = 0, n = ba.size(); i != n; ++i) { const unsigned char c = ba.at(i); if (isprint(c)) { res += c; } else { qsnprintf(buf, sizeof(buf) - 1, "\\x%02x", int(c)); res += buf; } } return res; } /////////////////////////////////////////////////////////////////////// // // LldbEngine // /////////////////////////////////////////////////////////////////////// LldbEngine::LldbEngine(const DebuggerStartParameters &startParameters) : DebuggerEngine(startParameters) { setObjectName(QLatin1String("LldbEngine")); m_pythonAttemptedToLoad = false; } LldbEngine::~LldbEngine() {} void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) { if (!(languages & CppLanguage)) return; QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); //XSDEBUG("LldbEngine::executeDebuggerCommand:" << command); if (state() == DebuggerNotReady) { showMessage(_("LLDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command); return; } QTC_ASSERT(m_lldbProc.state() == QProcess::Running, notifyEngineIll()); postCommand(command.toLatin1(), CB(handleExecuteDebuggerCommand)); } void LldbEngine::handleExecuteDebuggerCommand(const LldbResponse &response) { Q_UNUSED(response); } void LldbEngine::postDirectCommand(const QByteArray &command) { QTC_ASSERT(m_lldbProc.state() == QProcess::Running, notifyEngineIll()); showMessage(_(command), LogInput); m_lldbProc.write(command + '\n'); } void LldbEngine::postCommand(const QByteArray &command, LldbCommandCallback callback, const char *callbackName, const QVariant &cookie) { static int token = 0; ++token; QTC_ASSERT(m_lldbProc.state() == QProcess::Running, notifyEngineIll()); LldbCommand cmd; cmd.command = command; cmd.callback = callback; cmd.callbackName = callbackName; cmd.cookie = cookie; cmd.token = token; m_commands.enqueue(cmd); showMessage(QString::number(token) + _(cmd.command), LogInput); m_lldbProc.write(cmd.command + '\n'); } void LldbEngine::shutdownInferior() { QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state()); notifyInferiorShutdownOk(); } void LldbEngine::shutdownEngine() { QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state()); m_lldbProc.kill(); } void LldbEngine::setupEngine() { QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); m_lldb = startParameters().debuggerCommand; showMessage(_("STARTING LLDB ") + m_lldb); connect(&m_lldbProc, SIGNAL(error(QProcess::ProcessError)), SLOT(handleLldbError(QProcess::ProcessError))); connect(&m_lldbProc, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(handleLldbFinished(int,QProcess::ExitStatus))); connect(&m_lldbProc, SIGNAL(readyReadStandardOutput()), SLOT(readLldbStandardOutput())); connect(&m_lldbProc, SIGNAL(readyReadStandardError()), SLOT(readLldbStandardError())); connect(this, SIGNAL(outputReady(QByteArray)), SLOT(handleOutput2(QByteArray)), Qt::QueuedConnection); // We will stop immediately, so setup a proper callback. LldbCommand cmd; cmd.callback = &LldbEngine::handleFirstCommand; m_commands.enqueue(cmd); m_lldbProc.start(m_lldb); if (!m_lldbProc.waitForStarted()) { const QString msg = tr("Unable to start lldb '%1': %2") .arg(m_lldb, m_lldbProc.errorString()); notifyEngineSetupFailed(); showMessage(_("ADAPTER START FAILED")); if (!msg.isEmpty()) { const QString title = tr("Adapter start failed"); Core::ICore::showWarningWithOptions(title, msg); } notifyEngineSetupFailed(); return; } loadPythonDumpers(); postCommand("setting set auto-confirm on"); postCommand("setting set interpreter.prompt-on-quit off"); // Default: // frame-format (string) = "frame #${frame.index}: ${frame.pc} // { ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}} // { at ${line.file.basename}:${line.number}}\n" postCommand("settings set frame-format frame=\\{" "index='${frame.index}'," "pc='${frame.pc}'," "module='${module.file.basename}'," //"function='${function.name-with-args}'," "function='${function.name}'," "pcoffset='${function.pc-offset}'," "file='${line.file.basename}'," "line='${line.number}'" "\\},"); // Default: // "thread #${thread.index}: tid = ${thread.id}{, ${frame.pc}} // { ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}} // { at ${line.file.basename}:${line.number}} // {, stop reason = ${thread.stop-reason}}{\nReturn value: ${thread.return-value}}\n" postCommand("settings set thread-format thread=\\{" "index='${thread.index}'," "tid='${thread.id}'," "framepc='${frame.pc}'," "module='${module.file.basename}'," "function='${function.name}'," "pcoffset='${function.pc-offset}'," "stopreason='${thread.stop-reason}'" //"returnvalue='${thread.return-value}'" "\\},"); notifyEngineSetupOk(); } void LldbEngine::setupInferior() { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); QString fileName = QFileInfo(startParameters().executable).absoluteFilePath(); postCommand("target create " + fileName.toUtf8(), CB(handleInferiorSetup)); } void LldbEngine::handleInferiorSetup(const LldbResponse &response) { Q_UNUSED(response); notifyInferiorSetupOk(); } void LldbEngine::runEngine() { QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); attemptBreakpointSynchronization(); showStatusMessage(tr("Running requested..."), 5000); postCommand("process launch", CB(handleRunEngine)); } void LldbEngine::handleRunEngine(const LldbResponse &response) { Q_UNUSED(response); notifyEngineRunAndInferiorRunOk(); } void LldbEngine::interruptInferior() { showStatusMessage(tr("Interrupt requested..."), 5000); postCommand("process launch", CB(handleInferiorInterrupt)); } void LldbEngine::handleInferiorInterrupt(const LldbResponse &response) { Q_UNUSED(response); } void LldbEngine::executeStep() { resetLocation(); notifyInferiorRunRequested(); postCommand("thread step-in", CB(handleContinue)); } void LldbEngine::handleContinue(const LldbResponse &response) { Q_UNUSED(response); notifyInferiorRunOk(); } void LldbEngine::executeStepI() { resetLocation(); notifyInferiorRunRequested(); postCommand("thread step-inst", CB(handleContinue)); } void LldbEngine::executeStepOut() { resetLocation(); notifyInferiorRunRequested(); postCommand("thread step-out", CB(handleContinue)); } void LldbEngine::executeNext() { resetLocation(); notifyInferiorRunRequested(); postCommand("thread step-over", CB(handleContinue)); } void LldbEngine::executeNextI() { resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); postCommand("thread step-inst-over", CB(handleContinue)); } void LldbEngine::continueInferior() { resetLocation(); notifyInferiorRunRequested(); postCommand("process continue", CB(handleContinue)); } void LldbEngine::executeRunToLine(const ContextData &data) { Q_UNUSED(data) SDEBUG("FIXME: LldbEngine::runToLineExec()"); } void LldbEngine::executeRunToFunction(const QString &functionName) { Q_UNUSED(functionName) XSDEBUG("FIXME: LldbEngine::runToFunctionExec()"); } void LldbEngine::executeJumpToLine(const ContextData &data) { Q_UNUSED(data) XSDEBUG("FIXME: LldbEngine::jumpToLineExec()"); } void LldbEngine::activateFrame(int frameIndex) { resetLocation(); if (state() != InferiorStopOk && state() != InferiorUnrunnable) return; postCommand("frame select " + QByteArray::number(frameIndex), CB(handleUpdateAll)); } void LldbEngine::selectThread(ThreadId threadId) { postCommand("thread select " + QByteArray::number(threadId.raw()), CB(handleUpdateAll)); } bool LldbEngine::acceptsBreakpoint(BreakpointModelId id) const { return breakHandler()->breakpointData(id).isCppBreakpoint() && startParameters().startMode != AttachCore; } void LldbEngine::insertBreakpoint(BreakpointModelId id) { BreakHandler *handler = breakHandler(); QTC_CHECK(handler->state(id) == BreakpointInsertRequested); handler->notifyBreakpointInsertProceeding(id); QByteArray loc; if (handler->type(id) == BreakpointByFunction) loc = " --name " + handler->functionName(id).toLatin1(); else loc = " --file " + handler->fileName(id).toLocal8Bit() + " --line " + QByteArray::number(handler->lineNumber(id)); postCommand("break set " + loc, CB(handleBreakInsert), QVariant(id)); } void LldbEngine::handleBreakInsert(const LldbResponse &response) { //qDebug() << "BP RESPONSE: " << response.data; // Breakpoint 1: where = simple_test_app`main + 62 at simple_test_app.cpp:6699, // address = 0x08061664 BreakpointModelId id(response.cookie.toInt()); BreakHandler *handler = breakHandler(); QTC_ASSERT(response.data.startsWith("Breakpoint "), return); int pos1 = response.data.indexOf(':'); QTC_ASSERT(pos1 != -1, return); QByteArray bpnr = response.data.mid(11, pos1 - 11); int pos2 = response.data.lastIndexOf(':'); QByteArray file = response.data.mid(pos1 + 4, pos2 - pos1 - 4); QByteArray line = response.data.mid(pos2 + 1); BreakpointResponse br; br.id = BreakpointResponseId(bpnr); br.fileName = _(file); br.lineNumber = line.toInt(); handler->setResponse(id, br); QTC_CHECK(!handler->needsChange(id)); handler->notifyBreakpointInsertOk(id); } void LldbEngine::removeBreakpoint(BreakpointModelId id) { BreakHandler *handler = breakHandler(); QTC_CHECK(handler->state(id) == BreakpointRemoveRequested); handler->notifyBreakpointRemoveProceeding(id); BreakpointResponse br = handler->response(id); showMessage(_("DELETING BP %1 IN %2").arg(br.id.toString()) .arg(handler->fileName(id))); postCommand("break delete " + br.id.toByteArray()); // Pretend it succeeds without waiting for response. handler->notifyBreakpointRemoveOk(id); } void LldbEngine::loadSymbols(const QString &moduleName) { Q_UNUSED(moduleName) } void LldbEngine::loadAllSymbols() { } void LldbEngine::reloadModules() { //postCommand("qdebug('listmodules')", CB(handleListModules)); } void LldbEngine::handleListModules(const LldbResponse &response) { GdbMi out; out.fromString(response.data.trimmed()); Modules modules; foreach (const GdbMi &item, out.children()) { Module module; module.moduleName = _(item.findChild("name").data()); QString path = _(item.findChild("value").data()); int pos = path.indexOf(_("' from '")); if (pos != -1) { path = path.mid(pos + 8); if (path.size() >= 2) path.chop(2); } else if (path.startsWith(_(""))) { path = _("(builtin)"); } module.modulePath = path; modules.append(module); } modulesHandler()->setModules(modules); } void LldbEngine::requestModuleSymbols(const QString &moduleName) { postCommand("target module list", CB(handleListSymbols), moduleName); } void LldbEngine::handleListSymbols(const LldbResponse &response) { GdbMi out; out.fromString(response.data.trimmed()); Symbols symbols; QString moduleName = response.cookie.toString(); foreach (const GdbMi &item, out.children()) { Symbol symbol; symbol.name = _(item.findChild("name").data()); symbols.append(symbol); } debuggerCore()->showModuleSymbols(moduleName, symbols); } ////////////////////////////////////////////////////////////////////// // // Tooltip specific stuff // ////////////////////////////////////////////////////////////////////// static WatchData m_toolTip; static QPoint m_toolTipPos; static QHash m_toolTipCache; bool LldbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx) { Q_UNUSED(mousePos) Q_UNUSED(editor) if (state() != InferiorStopOk) { //SDEBUG("SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED"); return false; } // Check mime type and get expression (borrowing some C++ - functions) const QString javaPythonMimeType = QLatin1String("application/javascript"); if (!editor->document() || editor->document()->mimeType() != javaPythonMimeType) return false; int line; int column; QString exp = cppExpressionAt(editor, ctx.position, &line, &column); /* if (m_toolTipCache.contains(exp)) { const WatchData & data = m_toolTipCache[exp]; q->watchHandler()->removeChildren(data.iname); insertData(data); return; } */ QToolTip::hideText(); if (exp.isEmpty() || exp.startsWith(QLatin1Char('#'))) { QToolTip::hideText(); return false; } if (!hasLetterOrNumber(exp)) { QToolTip::showText(m_toolTipPos, tr("'%1' contains no identifier").arg(exp)); return true; } if (exp.startsWith(QLatin1Char('"')) && exp.endsWith(QLatin1Char('"'))) { QToolTip::showText(m_toolTipPos, tr("String literal %1").arg(exp)); return true; } if (exp.startsWith(QLatin1String("++")) || exp.startsWith(QLatin1String("--"))) exp.remove(0, 2); if (exp.endsWith(QLatin1String("++")) || exp.endsWith(QLatin1String("--"))) exp.remove(0, 2); if (exp.startsWith(QLatin1Char('<')) || exp.startsWith(QLatin1Char('['))) return false; if (hasSideEffects(exp)) { QToolTip::showText(m_toolTipPos, tr("Cowardly refusing to evaluate expression '%1' " "with potential side effects").arg(exp)); return true; } #if 0 //if (status() != InferiorStopOk) // return; // FIXME: 'exp' can contain illegal characters m_toolTip = WatchData(); m_toolTip.exp = exp; m_toolTip.name = exp; m_toolTip.iname = tooltipIName; insertData(m_toolTip); #endif return false; } ////////////////////////////////////////////////////////////////////// // // Watch specific stuff // ////////////////////////////////////////////////////////////////////// void LldbEngine::assignValueInDebugger(const Internal::WatchData *, const QString &expression, const QVariant &value) { Q_UNUSED(expression); Q_UNUSED(value); SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value.toString())); #if 0 m_scriptEngine->evaluate(expression + QLatin1Char('=') + value.toString()); updateLocals(); #endif } void LldbEngine::updateWatchData(const WatchData &data, const WatchUpdateFlags &flags) { Q_UNUSED(data); Q_UNUSED(flags); updateAll(); } void LldbEngine::handleLldbError(QProcess::ProcessError error) { qDebug() << "HANDLE LLDB ERROR"; showMessage(_("HANDLE LLDB ERROR")); switch (error) { case QProcess::Crashed: break; // will get a processExited() as well // impossible case QProcess::FailedToStart: case QProcess::ReadError: case QProcess::WriteError: case QProcess::Timedout: default: //setState(EngineShutdownRequested, true); m_lldbProc.kill(); showMessageBox(QMessageBox::Critical, tr("Lldb I/O Error"), errorMessage(error)); break; } } QString LldbEngine::errorMessage(QProcess::ProcessError error) const { switch (error) { case QProcess::FailedToStart: return tr("The Lldb process failed to start. Either the " "invoked program '%1' is missing, or you may have insufficient " "permissions to invoke the program.") .arg(m_lldb); case QProcess::Crashed: return tr("The Lldb process crashed some time after starting " "successfully."); case QProcess::Timedout: return tr("The last waitFor...() function timed out. " "The state of QProcess is unchanged, and you can try calling " "waitFor...() again."); case QProcess::WriteError: return tr("An error occurred when attempting to write " "to the Lldb process. For example, the process may not be running, " "or it may have closed its input channel."); case QProcess::ReadError: return tr("An error occurred when attempting to read from " "the Lldb process. For example, the process may not be running."); default: return tr("An unknown error in the Lldb process occurred. "); } } void LldbEngine::handleLldbFinished(int code, QProcess::ExitStatus type) { qDebug() << "LLDB FINISHED"; showMessage(_("LLDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code)); notifyEngineSpontaneousShutdown(); } void LldbEngine::readLldbStandardError() { QByteArray err = m_lldbProc.readAllStandardError(); qDebug() << "\nLLDB STDERR" << err; //qWarning() << "Unexpected lldb stderr:" << err; showMessage(_("Lldb stderr: " + err)); //handleOutput(err); } static bool isEatable(char c) { return c == 10 || c == 13; } void LldbEngine::readLldbStandardOutput() { QByteArray out = m_lldbProc.readAllStandardOutput(); showMessage(_("Lldb stdout: " + out)); qDebug("\nLLDB RAW STDOUT: '%s'", quoteUnprintable(out).constData()); // Remove embedded backspace characters int j = 1; const int n = out.size(); for (int i = 1; i < n; ++i, ++j) { const char c = out.at(i); if (i != j) out[j] = c; if (c == 8) j -= 2; else if (isEatable(c)) --j; } if (j != n) out.resize(j); //qDebug("\n\nLLDB STDOUT: '%s'", quoteUnprintable(out).constData()); if (out.isEmpty()) return; if (out.startsWith(8)) { if (!m_inbuffer.isEmpty()) m_inbuffer.chop(1); m_inbuffer.append(out.mid(1)); } else if (isEatable(out.at(0))) { m_inbuffer.append(out.mid(1)); } else { m_inbuffer.append(out); } qDebug("\nBUFFER FROM: '%s'", quoteUnprintable(m_inbuffer).constData()); while (true) { int pos = m_inbuffer.indexOf("(lldb) "); if (pos == -1) break; QByteArray response = m_inbuffer.left(pos).trimmed(); m_inbuffer = m_inbuffer.mid(pos + 7); qDebug("\nBUFFER RECOGNIZED: '%s'", quoteUnprintable(response).constData()); emit outputReady(response); } qDebug("\nBUFFER LEFT: '%s'", quoteUnprintable(m_inbuffer).constData()); if (m_inbuffer.isEmpty()) return; // Could be a 'stopped' message left. // "[KProcess 5564 stopped" // "* thread #1: tid = 0x15bc, 0x0804a17d untitled11`main(argc=1, // argv=0xbfffeff4) + 61 at main.cpp:52, stop reason = breakpoint 1.1 ..." const int pos = m_inbuffer.indexOf("\x1b[KProcess"); if (pos != -1) { int pid = 0; const char *format = "\x1b[KProcess %d stopped"; if (::sscanf(m_inbuffer.constData() + pos, format, &pid) == 1) { notifyInferiorSpontaneousStop(); m_inbuffer.clear(); updateAll(); } } } void LldbEngine::handleOutput2(const QByteArray &data) { LldbResponse response; response.data = data; showMessage(_(data)); QTC_ASSERT(!m_commands.isEmpty(), qDebug() << "RESPONSE: " << data; return); LldbCommand cmd = m_commands.dequeue(); // FIXME: Find a way to tell LLDB to no echo input. if (response.data.startsWith(cmd.command)) response.data = response.data.mid(cmd.command.size()); response.cookie = cmd.cookie; qDebug("\nDEQUE: '%s' -> '%s'", cmd.command.constData(), cmd.callbackName); if (cmd.callback) { //qDebug() << "EXECUTING CALLBACK " << cmd.callbackName // << " RESPONSE: " << response.data; (this->*cmd.callback)(response); } else { qDebug() << "NO CALLBACK FOR RESPONSE: " << response.data; } } void LldbEngine::handleFirstCommand(const LldbResponse &response) { Q_UNUSED(response); } void LldbEngine::handleUpdateAll(const LldbResponse &response) { Q_UNUSED(response); updateAll(); } void LldbEngine::updateAll() { //postCommand("bt", CB(handleBacktrace)); updateLocals(); } void LldbEngine::updateLocals() { QByteArray watchers; //if (!m_toolTipExpression.isEmpty()) // watchers += m_toolTipExpression.toLatin1() // + '#' + tooltipINameForExpression(m_toolTipExpression.toLatin1()); WatchHandler *handler = watchHandler(); QHash watcherNames = handler->watcherNames(); QHashIterator it(watcherNames); while (it.hasNext()) { it.next(); if (!watchers.isEmpty()) watchers += "##"; watchers += it.key() + "#watch." + QByteArray::number(it.value()); } QByteArray options; if (debuggerCore()->boolSetting(UseDebuggingHelpers)) options += "fancy,"; if (debuggerCore()->boolSetting(AutoDerefPointers)) options += "autoderef,"; if (options.isEmpty()) options += "defaults,"; options.chop(1); postCommand("script bb('options:" + options + " " + "vars: " + "expanded:" + handler->expansionRequests() + " " + "typeformats:" + handler->typeFormatRequests() + " " + "formats:" + handler->individualFormatRequests() + " " + "watcher:" + watchers.toHex() + "')", CB(handleListLocals)); } void LldbEngine::handleBacktrace(const LldbResponse &response) { // Populate stack view. StackFrames stackFrames; int level = 0; int currentIndex = -1; foreach (const QByteArray &line, response.data.split('\n')) { //qDebug() << " LINE: '" << line << "'"; if (line.startsWith("> ") || line.startsWith(" ")) { int pos1 = line.indexOf('('); int pos2 = line.indexOf(')', pos1); if (pos1 != -1 && pos2 != -1) { int lineNumber = line.mid(pos1 + 1, pos2 - pos1 - 1).toInt(); QByteArray fileName = line.mid(2, pos1 - 2); //qDebug() << " " << pos1 << pos2 << lineNumber << fileName // << line.mid(pos1 + 1, pos2 - pos1 - 1); StackFrame frame; frame.file = _(fileName); frame.line = lineNumber; frame.function = _(line.mid(pos2 + 1)); frame.usable = QFileInfo(frame.file).isReadable(); if (frame.line > 0 && QFileInfo(frame.file).exists()) { if (line.startsWith("> ")) currentIndex = level; frame.level = level; stackFrames.prepend(frame); ++level; } } } } const int frameCount = stackFrames.size(); for (int i = 0; i != frameCount; ++i) stackFrames[i].level = frameCount - stackFrames[i].level - 1; stackHandler()->setFrames(stackFrames); // Select current frame. if (currentIndex != -1) { currentIndex = frameCount - currentIndex - 1; stackHandler()->setCurrentIndex(currentIndex); gotoLocation(stackFrames.at(currentIndex)); } updateLocals(); } void LldbEngine::handleListLocals(const LldbResponse &response) { //qDebug() << " LOCALS: '" << response.data << "'"; QByteArray out = response.data; { int pos = out.indexOf("data="); if (pos == -1) { showMessage(_("UNEXPECTED LOCALS OUTPUT:" + out)); return; } // The value in 'out' should be single-quoted as this is // what the command line does with strings. if (pos != 1) { showMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: " + out.left(pos))); } out = out.mid(pos); if (out.endsWith('\'')) out.chop(1); else showMessage(_("JUNK AT END OF RESPONSE: " + out)); } GdbMi all; all.fromStringMultiple(out); GdbMi data = all.findChild("data"); const bool partial = response.cookie.toBool(); WatchHandler *handler = watchHandler(); QList list; if (!partial) { list.append(*handler->findData("local")); list.append(*handler->findData("watch")); list.append(*handler->findData("return")); } foreach (const GdbMi &child, data.children()) { WatchData dummy; dummy.iname = child.findChild("iname").data(); GdbMi wname = child.findChild("wname"); if (wname.isValid()) { // Happens (only) for watched expressions. They are encoded as // base64 encoded 8 bit data, without quotes dummy.name = decodeData(wname.data(), Base64Encoded8Bit); dummy.exp = dummy.name.toUtf8(); } else { dummy.name = _(child.findChild("name").data()); } parseWatchData(handler->expandedINames(), dummy, child, &list); } // const GdbMi typeInfo = all.findChild("typeinfo"); // if (typeInfo.type() == GdbMi::List) { // foreach (const GdbMi &s, typeInfo.children()) { // const GdbMi name = s.findChild("name"); // const GdbMi size = s.findChild("size"); // if (name.isValid() && size.isValid()) // m_typeInfoCache.insert(QByteArray::fromBase64(name.data()), // TypeInfo(size.data().toUInt())); // } // } // for (int i = 0; i != list.size(); ++i) { // const TypeInfo ti = m_typeInfoCache.value(list.at(i).type); // if (ti.size) // list[i].size = ti.size; // } handler->insertData(list); //rebuildWatchModel(); if (!partial) emit stackFrameCompleted(); } void LldbEngine::loadPythonDumpers() { if (m_pythonAttemptedToLoad) return; m_pythonAttemptedToLoad = true; const QByteArray dumperSourcePath = Core::ICore::resourcePath().toLocal8Bit() + "/dumper/"; postCommand("script execfile('" + dumperSourcePath + "bridge.py')"); postCommand("script execfile('" + dumperSourcePath + "dumper.py')"); postCommand("script execfile('" + dumperSourcePath + "qttypes.py')"); postCommand("script bbsetup('')"); } bool LldbEngine::hasCapability(unsigned cap) const { return cap & (ReloadModuleCapability|BreakConditionCapability); } DebuggerEngine *createLldbEngine(const DebuggerStartParameters &startParameters) { return new LldbEngine(startParameters); } } // namespace Internal } // namespace Debugger