diff --git a/share/qtcreator/debugger/pdbbridge.py b/share/qtcreator/debugger/pdbbridge.py index 32de9c002ed..07bdbf660c6 100644 --- a/share/qtcreator/debugger/pdbbridge.py +++ b/share/qtcreator/debugger/pdbbridge.py @@ -1,252 +1,284 @@ -import pdb; -import sys; +import pdb +import sys import linecache +import inspect +import os + +class Dumper: + def __init__(self): + pass + + def updateData(self, args): + self.expandedINames = set(args.get("expanded", [])) + self.typeformats = args.get("typeformats", {}) + self.formats = args.get("formats", {}) + self.watchers = args.get("watchers", {}) + self.output = "data={" + # self.handleListModules() + # self.handleListSymbols(expanded) + + # Trigger error to get a backtrace. + frame = None + #self.warn("frame: %s" % frame) + try: + raise ZeroDivisionError + except ZeroDivisionError: + frame = sys.exc_info()[2].tb_frame.f_back + + limit = 30 + n = 0 + isActive = False + while frame is not None and n < limit: + #self.warn("frame: %s" % frame.f_locals.keys()) + lineno = frame.f_lineno + code = frame.f_code + filename = code.co_filename + name = code.co_name + if isActive: + linecache.checkcache(filename) + line = linecache.getline(filename, lineno, frame.f_globals) + self.dumpFrame(frame) + if name == "": + isActive = False + if name == "trace_dispatch": + isActive = True + frame = frame.f_back + n = n + 1 + #sys.stdout.flush() + + self.output += '}' + sys.stdout.write(self.output) + + def put(self, value): + #sys.stdout.write(value) + self.output += value + + def putField(self, name, value): + self.put('%s="%s",' % (name, value)) + + def putItemCount(self, count): + self.put('value="<%s items>",' % count) + + def putEllipsis(self): + self.put('{name="",value="",type="",numchild="0"},') + + def cleanType(self, type): + t = str(type) + if t.startswith(""): + t = t[7:-2] + if t.startswith(""): + t = t[8:-2] + return t + + def putType(self, type, priority = 0): + self.putField("type", self.cleanType(type)) + + def putAddress(self, addr): + self.put('addr="%s",' % cleanAddress(addr)) + + def putNumChild(self, numchild): + self.put('numchild="%s",' % numchild) + + def putValue(self, value, encoding = None, priority = 0): + self.putField("value", value) + + def putName(self, name): + self.put('name="%s",' % name) + + def isExpanded(self, iname): + #self.warn("IS EXPANDED: %s in %s" % (iname, self.expandedINames)) + if iname.startswith("None"): + raise "Illegal iname '%s'" % iname + #self.warn(" --> %s" % (iname in self.expandedINames)) + return iname in self.expandedINames + + def isExpandedIName(self, iname): + return iname in self.expandedINames + + def itemFormat(self, item): + format = self.formats.get(str(cleanAddress(item.value.address))) + if format is None: + format = self.typeformats.get(self.stripClassTag(str(item.value.type))) + return format + + def dumpFrame(self, frame): + for var in frame.f_locals.keys(): + if var == "__file__": + continue + #if var == "__name__": + # continue + if var == "__package__": + continue + if var == "qdebug": + continue + if var != '__builtins__': + value = frame.f_locals[var] + self.dumpValue(value, var, "local.%s" % var) + + def dumpValue(self, value, name, iname): + t = type(value) + tt = self.cleanType(t) + if tt == "module" or tt == "function": + return + if str(value).startswith(" 1: + v = "@" + v[p + 11:-1] + self.putValue(v) + if self.isExpanded(iname): + self.put("children=[") + for child in dir(value): + if child == "__dict__": + continue + if child == "__doc__": + continue + if child == "__module__": + continue + attr = getattr(value, child) + if callable(attr): + continue + try: + self.dumpValue(attr, child, "%s.%s" % (iname, child)) + except: + pass + self.put("],") + self.put("},") + + + def warn(self, msg): + self.putField("warning", msg) + + def handleListModules(self): + self.put("modules=["); + for name in sys.modules: + self.put("{") + self.putName(name) + self.putValue(sys.modules[name]) + self.put("},") + self.put("]") + #sys.stdout.flush() + + def handleListSymbols(self, module): + #self.put("symbols=%s" % dir(sys.modules[module])) + self.put("symbols=["); + for name in sys.modules: + self.put("{") + self.putName(name) + #self.putValue(sys.modules[name]) + self.put("},") + self.put("]") + #sys.stdout.flush() + + def stackListFrames(self, args): + #isNativeMixed = int(args.get('nativeMixed', 0)) + #result = 'stack={current-thread="%s"' % thread.GetThreadID() + result = 'stack={current-thread="%s"' % 1 + result += ',frames=[' + + level = 0 + for frame in inspect.stack(): + l = [i for i in frame] + fileName = l[1] + (head, tail) = os.path.split(fileName) + if tail in ("pdbbridge.py", "bdb.py", "pdb.py", "cmd.py", ""): + continue + + level += 1 + result += '{' + result += 'a0="%s",' % l[0] + result += 'file="%s",' % fileName + result += 'line="%s",' % l[2] + result += 'a3="%s",' % l[3] + result += 'a4="%s",' % l[4] + result += 'a5="%s",' % l[5] + result += 'level="%s",' % level + result += '}' + + result += ']' + #result += ',hasmore="%d"' % isLimited + #result += ',limit="%d"' % limit + result += '}' + self.report(result) + + + def report(self, stuff): + sys.stdout.write("@\n" + stuff + "@\n") + sys.stdout.flush() def qdebug(options = None, expanded = None, typeformats = None, individualformats = None, watchers = None): - - class QDebug: - def __init__(self, - options = None, - expanded = None, - typeformats = None, - individualformats = None, - watchers = None): - self.options = options - self.expandedINames = expanded - self.typeformats = typeformats - self.individualformats = individualformats - self.watchers = watchers - self.buffer = "" - if self.options == "listmodules": - self.handleListModules() - elif self.options == "listsymbols": - self.handleListSymbols(expanded) - else: - self.handleListVars() - - def put(self, value): - #sys.stdout.write(value) - self.buffer += value - - def putField(self, name, value): - self.put('%s="%s",' % (name, value)) - - def putItemCount(self, count): - self.put('value="<%s items>",' % count) - - def putEllipsis(self): - self.put('{name="",value="",type="",numchild="0"},') - - def cleanType(self, type): - t = str(type) - if t.startswith(""): - t = t[7:-2] - if t.startswith(""): - t = t[8:-2] - return t - - def putType(self, type, priority = 0): - self.putField("type", self.cleanType(type)) - - def putAddress(self, addr): - self.put('addr="%s",' % cleanAddress(addr)) - - def putNumChild(self, numchild): - self.put('numchild="%s",' % numchild) - - def putValue(self, value, encoding = None, priority = 0): - self.putField("value", value) - - def putName(self, name): - self.put('name="%s",' % name) - - def isExpanded(self, iname): - #self.warn("IS EXPANDED: %s in %s" % (iname, self.expandedINames)) - if iname.startswith("None"): - raise "Illegal iname '%s'" % iname - #self.warn(" --> %s" % (iname in self.expandedINames)) - return iname in self.expandedINames - - def isExpandedIName(self, iname): - return iname in self.expandedINames - - def itemFormat(self, item): - format = self.formats.get(str(cleanAddress(item.value.address))) - if format is None: - format = self.typeformats.get(self.stripClassTag(str(item.value.type))) - return format - - def dumpFrame(self, frame): - for var in frame.f_locals.keys(): - if var == "__file__": - continue - #if var == "__name__": - # continue - if var == "__package__": - continue - if var == "qdebug": - continue - if var != '__builtins__': - value = frame.f_locals[var] - self.dumpValue(value, var, "local.%s" % var) - - def dumpValue(self, value, name, iname): - t = type(value) - tt = self.cleanType(t) - if tt == "module" or tt == "function": - return - if str(value).startswith(" 1: - v = "@" + v[p + 11:-1] - self.putValue(v) - if self.isExpanded(iname): - self.put("children=[") - for child in dir(value): - if child == "__dict__": - continue - if child == "__doc__": - continue - if child == "__module__": - continue - attr = getattr(value, child) - if callable(attr): - continue - try: - self.dumpValue(attr, child, "%s.%s" % (iname, child)) - except: - pass - self.put("],") - self.put("},") - - - def warn(self, msg): - self.putField("warning", msg) - - def handleListVars(self): - # Trigger error to get a backtrace. - frame = None - #self.warn("frame: %s" % frame) - try: - raise ZeroDivisionError - except ZeroDivisionError: - frame = sys.exc_info()[2].tb_frame.f_back - - limit = 30 - n = 0 - isActive = False - while frame is not None and n < limit: - #self.warn("frame: %s" % frame.f_locals.keys()) - lineno = frame.f_lineno - code = frame.f_code - filename = code.co_filename - name = code.co_name - if isActive: - linecache.checkcache(filename) - line = linecache.getline(filename, lineno, frame.f_globals) - self.dumpFrame(frame) - if name == "": - isActive = False - if name == "trace_dispatch": - isActive = True - frame = frame.f_back - n = n + 1 - #sys.stdout.flush() - - def handleListModules(self): - self.put("modules=["); - for name in sys.modules: - self.put("{") - self.putName(name) - self.putValue(sys.modules[name]) - self.put("},") - self.put("]") - #sys.stdout.flush() - - def handleListSymbols(self, module): - #self.put("symbols=%s" % dir(sys.modules[module])) - self.put("symbols=["); - for name in sys.modules: - self.put("{") - self.putName(name) - #self.putValue(sys.modules[name]) - self.put("},") - self.put("]") - #sys.stdout.flush() - - d = QDebug(options, expanded, typeformats, individualformats, watchers) - #print d.buffer - sys.stdout.write(d.buffer) + sys.stdout.write("\n(Pdb)\n") sys.stdout.flush() + +theDumper = Dumper() diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 0365782849d..b3041de9424 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -132,6 +132,14 @@ void PdbEngine::postCommand(const QByteArray &command, DebuggerCommand::Callback m_pdbProc.write(cmd.function + '\n'); } +void PdbEngine::runCommand(const DebuggerCommand &cmd) +{ + QTC_ASSERT(m_pdbProc.state() == QProcess::Running, notifyEngineIll()); + QByteArray command = "theDumper." + cmd.function + "({" + cmd.args + "})"; + showMessage(_(command), LogInput); + m_pdbProc.write(command + '\n'); +} + void PdbEngine::shutdownInferior() { QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state()); @@ -361,6 +369,8 @@ void PdbEngine::handleBreakInsert(const DebuggerResponse &response, Breakpoint b br.id = BreakpointResponseId(bpnr); br.fileName = _(file); br.lineNumber = line.toInt(); + if (!bp.isValid()) + bp = breakHandler()->findBreakpointByFileAndLine(br.fileName, br.lineNumber, false); bp.setResponse(br); QTC_CHECK(!bp.needsChange()); bp.notifyBreakpointInsertOk(); @@ -444,7 +454,6 @@ void PdbEngine::handleListSymbols(const DebuggerResponse &response, const QStrin static WatchData m_toolTip; static QPoint m_toolTipPos; -static QHash m_toolTipCache; bool PdbEngine::setToolTipExpression(TextEditor::TextEditorWidget *editorWidget, const DebuggerToolTipContext &ctx) @@ -626,19 +635,71 @@ void PdbEngine::handleOutput(const QByteArray &data) void PdbEngine::handleOutput2(const QByteArray &data) { - DebuggerResponse response; - response.logStreamOutput = data; - showMessage(_(data)); - QTC_ASSERT(!m_commands.isEmpty(), qDebug() << "RESPONSE: " << data; return); - DebuggerCommand cmd = m_commands.dequeue(); - qDebug() << "DEQUE: " << cmd.function; - if (cmd.callback) { - //qDebug() << "EXECUTING CALLBACK " << cmd.callbackName - // << " RESPONSE: " << response.data; - cmd.callback(response); - } else { - qDebug() << "NO CALLBACK FOR RESPONSE: " << response.logStreamOutput; + QByteArray lineContext; + foreach (QByteArray line, data.split('\n')) { +// line = line.trimmed(); + + DebuggerResponse response; + response.logStreamOutput = line; + response.data.fromString(line); + showMessage(_("LINE: " + line)); + + if (line.startsWith("stack={")) { + refreshStack(response.data); + continue; + } + if (line.startsWith("data={")) { + refreshLocals(response.data); + continue; + } + if (line.startsWith("Breakpoint")) { + handleBreakInsert(response, Breakpoint()); + continue; + } + + if (line.startsWith("> /")) { + lineContext = line; + 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; + if (state() == InferiorRunOk) { + showMessage(QString::fromLatin1("STOPPED AT: %1:%2").arg(frame.file).arg(frame.line)); + gotoLocation(frame); + notifyInferiorSpontaneousStop(); + updateAll(); + continue; + } + } + } + + if (line.startsWith("-> ")) { + // Current line + } + + showMessage(_(" #### ... UNHANDLED")); } + + +// DebuggerResponse response; +// response.logStreamOutput = data; +// showMessage(_(data)); +// QTC_ASSERT(!m_commands.isEmpty(), qDebug() << "RESPONSE: " << data; return); +// DebuggerCommand cmd = m_commands.dequeue(); +// qDebug() << "DEQUE: " << cmd.function; +// if (cmd.callback) { +// //qDebug() << "EXECUTING CALLBACK " << cmd.callbackName +// // << " RESPONSE: " << response.data; +// cmd.callback(response); +// } else { +// qDebug() << "NO CALLBACK FOR RESPONSE: " << response.logStreamOutput; +// } } /* void PdbEngine::handleResponse(const QByteArray &response0) @@ -653,28 +714,65 @@ void PdbEngine::handleResponse(const QByteArray &response0) qDebug() << "SKIPPING '--Return--' MARKER"; response = response.mid(11); } - if (response.startsWith("> ")) { - int pos1 = response.indexOf('('); - int pos2 = response.indexOf(')', pos1); - if (pos1 != -1 && pos2 != -1) { - int lineNumber = response.mid(pos1 + 1, pos2 - pos1 - 1).toInt(); - QByteArray fileName = response.mid(2, pos1 - 2); - qDebug() << " " << pos1 << pos2 << lineNumber << fileName - << response.mid(pos1 + 1, pos2 - pos1 - 1); - StackFrame frame; - frame.file = _(fileName); - frame.line = lineNumber; - if (frame.line > 0 && QFileInfo(frame.file).exists()) { - gotoLocation(frame); - notifyInferiorSpontaneousStop(); - return; - } - } - } - qDebug() << "COULD NOT PARSE RESPONSE: '" << response << "'"; } */ +void PdbEngine::refreshLocals(const GdbMi &vars) +{ + //const bool partial = response.cookie.toBool(); + WatchHandler *handler = watchHandler(); + handler->resetValueCache(); + + QSet toDelete; + foreach (WatchItem *item, handler->model()->treeLevelItems(2)) + toDelete.insert(item->d.iname); + + foreach (const GdbMi &child, vars.children()) { + WatchItem *item = new WatchItem(child); + handler->insertItem(item); + toDelete.remove(item->d.iname); + } + + handler->purgeOutdatedItems(toDelete); + + DebuggerToolTipManager::updateEngine(this); +} + +void PdbEngine::refreshStack(const GdbMi &stack) +{ + StackHandler *handler = stackHandler(); + StackFrames frames; + foreach (const GdbMi &item, stack["frames"].children()) { + StackFrame frame; + frame.level = item["level"].toInt(); + frame.file = item["file"].toUtf8(); + frame.function = item["func"].toUtf8(); + frame.from = item["func"].toUtf8(); + frame.line = item["line"].toInt(); + frame.address = item["addr"].toAddress(); + GdbMi usable = item["usable"]; + if (usable.isValid()) + frame.usable = usable.data().toInt(); + else + frame.usable = QFileInfo(frame.file).isReadable(); + if (item["language"].data() == "js" + || frame.file.endsWith(QLatin1String(".js")) + || frame.file.endsWith(QLatin1String(".qml"))) { + frame.language = QmlLanguage; + frame.fixQmlFrame(startParameters()); + } + frames.append(frame); + } + bool canExpand = stack["hasmore"].toInt(); + //action(ExpandStack)->setEnabled(canExpand); + handler->setFrames(frames, canExpand); + + int index = stackHandler()->firstUsableIndex(); + handler->setCurrentIndex(index); + if (index >= 0 && index < handler->stackSize()) + gotoLocation(handler->frameAt(index)); +} + void PdbEngine::handleFirstCommand(const DebuggerResponse &response) { Q_UNUSED(response); @@ -689,100 +787,50 @@ void PdbEngine::handleUpdateAll(const DebuggerResponse &response) void PdbEngine::updateAll() { - postCommand("bt", CB(handleBacktrace)); - //updateLocals(); + postCommand("dir()"); + postCommand("theDumper.stackListFrames({})"); + updateLocals(); } void PdbEngine::updateLocals() { - QByteArray watchers; - //if (!m_toolTipExpression.isEmpty()) - // watchers += m_toolTipExpression.toLatin1() - // + '#' + tooltipINameForExpression(m_toolTipExpression.toLatin1()); + DebuggerCommand cmd("updateData"); + cmd.arg("nativeMixed", isNativeMixedActive()); + watchHandler()->appendFormatRequests(&cmd); - WatchHandler *handler = watchHandler(); - QHash watcherNames = handler->watcherNames(); - QHashIterator it(watcherNames); + const static bool alwaysVerbose = !qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty(); + cmd.arg("passexceptions", alwaysVerbose); + cmd.arg("fancy", boolSetting(UseDebuggingHelpers)); +// cmd.arg("partial", params.tryPartial); + + cmd.beginList("watchers"); + + // Watchers + QHashIterator it(WatchHandler::watcherNames()); while (it.hasNext()) { it.next(); - if (!watchers.isEmpty()) - watchers += "##"; - watchers += it.key() + "#watch." + QByteArray::number(it.value()); + cmd.beginGroup(); + cmd.arg("iname", "watch." + QByteArray::number(it.value())); + cmd.arg("exp", it.key().toHex()); + cmd.endGroup(); } - QByteArray options; - if (boolSetting(UseDebuggingHelpers)) - options += "fancy,"; - if (boolSetting(AutoDerefPointers)) - options += "autoderef,"; - if (options.isEmpty()) - options += "defaults,"; - options.chop(1); - - postCommand("qdebug('" + options + "','" -// + handler->expansionRequests() + "','" - + handler->typeFormatRequests() + "','" - + handler->individualFormatRequests() + "','" - + watchers.toHex() + "')", CB(handleListLocals)); -} - -void PdbEngine::handleBacktrace(const DebuggerResponse &response) -{ - //qDebug() << " BACKTRACE: '" << response.data << "'"; - // " /usr/lib/python2.6/bdb.py(368)run()" - // "-> exec cmd in globals, locals" - // " (1)()" - // " /python/math.py(19)()" - // "-> main()" - // " /python/math.py(14)main()" - // "-> print cube(3)" - // " /python/math.py(7)cube()" - // "-> x = square(a)" - // "> /python/math.py(2)square()" - // "-> def square(a):" - - // Populate stack view. - StackFrames stackFrames; - int level = 0; - int currentIndex = -1; - foreach (const QByteArray &line, response.logStreamOutput.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::exists(frame.file)) { - 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)); + // Tooltips + DebuggerToolTipContexts toolTips = DebuggerToolTipManager::pendingTooltips(this); + foreach (const DebuggerToolTipContext &p, toolTips) { + cmd.beginGroup(); + cmd.arg("iname", p.iname); + cmd.arg("exp", p.expression.toLatin1().toHex()); + cmd.endGroup(); } - updateLocals(); + cmd.endList(); + + //cmd.arg("resultvarname", m_resultVarName); + //m_lastDebuggableCommand = cmd; + //m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1"); + + runCommand(cmd); } void PdbEngine::handleListLocals(const DebuggerResponse &response) diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h index b434cca6d38..de99e6f03ec 100644 --- a/src/plugins/debugger/pdb/pdbengine.h +++ b/src/plugins/debugger/pdb/pdbengine.h @@ -54,6 +54,10 @@ public: explicit PdbEngine(const DebuggerStartParameters &startParameters); ~PdbEngine(); + void refreshStack(const GdbMi &stack); + void runCommand(const DebuggerCommand &cmd); + void refreshLocals(const GdbMi &vars); + private: // DebuggerEngine implementation void executeStep(); @@ -122,7 +126,6 @@ private: void handleFirstCommand(const DebuggerResponse &response); void handleExecuteDebuggerCommand(const DebuggerResponse &response); void handleStop(const DebuggerResponse &response); - void handleBacktrace(const DebuggerResponse &response); void handleListLocals(const DebuggerResponse &response); void handleListModules(const DebuggerResponse &response); void handleListSymbols(const DebuggerResponse &response, const QString &moduleName);