From 400eecf4efc8b46c231b5385dbd6830f9d2e39f7 Mon Sep 17 00:00:00 2001
From: hjk <qtc-committer@nokia.com>
Date: Fri, 23 Apr 2010 12:12:22 +0200
Subject: [PATCH] debugger: some work on python

---
 src/plugins/debugger/debugger.pro            |   1 +
 src/plugins/debugger/debuggermanager.cpp     |  21 +-
 src/plugins/debugger/debuggermanager.h       |   4 +
 src/plugins/debugger/pdb/pdb.pri             |   9 +
 src/plugins/debugger/pdb/pdbengine.cpp       | 781 +++++++++++++++++++
 src/plugins/debugger/pdb/pdbengine.h         | 160 ++++
 src/plugins/debugger/script/scriptengine.cpp |   2 +-
 tests/manual/gdbdebugger/python/math.py      |  19 +
 tests/manual/gdbdebugger/python/python.pro   |   4 +
 9 files changed, 999 insertions(+), 2 deletions(-)
 create mode 100644 src/plugins/debugger/pdb/pdb.pri
 create mode 100644 src/plugins/debugger/pdb/pdbengine.cpp
 create mode 100644 src/plugins/debugger/pdb/pdbengine.h
 create mode 100644 tests/manual/gdbdebugger/python/math.py
 create mode 100644 tests/manual/gdbdebugger/python/python.pro

diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro
index c93cd385fc7..3c9f4558fe1 100644
--- a/src/plugins/debugger/debugger.pro
+++ b/src/plugins/debugger/debugger.pro
@@ -100,6 +100,7 @@ LIBS  *= -lole32 \
 include(cdb/cdb.pri)
 include(gdb/gdb.pri)
 include(script/script.pri)
+include(python/python.pri)
 include(shared/shared.pri)
 
 OTHER_FILES += Debugger.pluginspec
diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp
index 6365b500695..5abbfb1214c 100644
--- a/src/plugins/debugger/debuggermanager.cpp
+++ b/src/plugins/debugger/debuggermanager.cpp
@@ -155,6 +155,7 @@ namespace Internal {
 
 IDebuggerEngine *createGdbEngine(DebuggerManager *parent);
 IDebuggerEngine *createScriptEngine(DebuggerManager *parent);
+IDebuggerEngine *createPythonEngine(DebuggerManager *parent);
 
 // The createWinEngine function takes a list of options pages it can add to.
 // This allows for having a "enabled" toggle on the page independently
@@ -250,6 +251,7 @@ void DebuggerStartParameters::clear()
 static Debugger::Internal::IDebuggerEngine *gdbEngine = 0;
 static Debugger::Internal::IDebuggerEngine *scriptEngine = 0;
 static Debugger::Internal::IDebuggerEngine *winEngine = 0;
+static Debugger::Internal::IDebuggerEngine *pythonEngine = 0;
 
 struct DebuggerManagerPrivate
 {
@@ -647,8 +649,14 @@ QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTy
         scriptEngine->addOptionPages(&rc);
     }
 
+    if (enabledTypeFlags & PythonEngineType) {
+        pythonEngine = createPythonEngine(this);
+        //pythonEngine->addOptionPages(&rc);
+    }
+
     d->m_engine = 0;
-    STATE_DEBUG(gdbEngine << winEngine << scriptEngine << rc.size());
+    STATE_DEBUG(gdbEngine << winEngine << scriptEngine
+        << pythonEngine << rc.size());
     return rc;
 }
 
@@ -842,6 +850,7 @@ void DebuggerManager::shutdown()
 
     #define doDelete(ptr) delete ptr; ptr = 0
     doDelete(scriptEngine);
+    doDelete(pythonEngine);
     doDelete(gdbEngine);
     doDelete(winEngine);
 
@@ -1006,6 +1015,14 @@ static IDebuggerEngine *debuggerEngineForExecutable(const QString &executable,
         return scriptEngine;
     }
 
+    if (executable.endsWith(_(".py"))) {
+        if (!pythonEngine) {
+            *errorMessage = msgEngineNotAvailable("Python Engine");
+            return 0;
+        }
+        return pythonEngine;
+    }
+
 #ifndef Q_OS_WIN
     Q_UNUSED(settingsIdHint)
     if (!gdbEngine) {
@@ -1080,6 +1097,8 @@ void DebuggerManager::startNewDebugger(const DebuggerStartParametersPtr &sp)
 
     if (sp->executable.endsWith(_(".js")))
         d->m_engine = scriptEngine;
+    else if (sp->executable.endsWith(_(".py")))
+        d->m_engine = pythonEngine;
     else
         d->m_engine = debuggerEngineForToolChain(sp->toolChainType);
 
diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h
index 5eb42211077..a035c85752d 100644
--- a/src/plugins/debugger/debuggermanager.h
+++ b/src/plugins/debugger/debuggermanager.h
@@ -83,6 +83,7 @@ class WatchHandler;
 class IDebuggerEngine;
 class GdbEngine;
 class ScriptEngine;
+class PythonEngine;
 class CdbDebugEngine;
 class CdbDebugEnginePrivate;
 struct DebuggerManagerActions;
@@ -138,9 +139,11 @@ enum DebuggerEngineTypeFlags
     GdbEngineType     = 0x01,
     ScriptEngineType  = 0x02,
     CdbEngineType     = 0x04,
+    PythonEngineType  = 0x08,
     AllEngineTypes = GdbEngineType
         | ScriptEngineType
         | CdbEngineType
+        | PythonEngineType
 };
 
 QDebug operator<<(QDebug d, DebuggerState state);
@@ -167,6 +170,7 @@ public:
     friend class Internal::CdbExceptionLoggerEventCallback;
     friend class Internal::GdbEngine;
     friend class Internal::ScriptEngine;
+    friend class Internal::PythonEngine;
     friend class Internal::CdbDebugEngine;
     friend class Internal::CdbDebugEnginePrivate;
     friend class Internal::TrkGdbAdapter;
diff --git a/src/plugins/debugger/pdb/pdb.pri b/src/plugins/debugger/pdb/pdb.pri
new file mode 100644
index 00000000000..47d1fbfdf66
--- /dev/null
+++ b/src/plugins/debugger/pdb/pdb.pri
@@ -0,0 +1,9 @@
+HEADERS += \
+    $$PWD/pdbengine.h \
+
+SOURCES += \
+    $$PWD/pdbengine.cpp \
+
+FORMS +=
+
+RESOURCES +=
diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp
new file mode 100644
index 00000000000..c33cfd450a8
--- /dev/null
+++ b/src/plugins/debugger/pdb/pdbengine.cpp
@@ -0,0 +1,781 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#define QT_NO_CAST_FROM_ASCII
+
+#include "pdbengine.h"
+
+#include "debuggeractions.h"
+#include "debuggerdialogs.h"
+#include "breakhandler.h"
+#include "debuggerconstants.h"
+#include "debuggermanager.h"
+#include "moduleshandler.h"
+#include "registerhandler.h"
+#include "stackhandler.h"
+#include "watchhandler.h"
+#include "watchutils.h"
+#include "debuggerstringutils.h"
+#include "../gdb/gdbmi.h"
+
+#include <utils/qtcassert.h>
+
+#include <texteditor/itexteditor.h>
+#include <coreplugin/ifile.h>
+//#include <coreplugin/scriptmanager/scriptmanager.h>
+#include <coreplugin/icore.h>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTimer>
+#include <QtCore/QVariant>
+
+#include <QtGui/QApplication>
+#include <QtGui/QToolTip>
+#include <QtGui/QMessageBox>
+
+
+#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) &PdbEngine::callback, STRINGIFY(callback)
+
+namespace Debugger {
+namespace Internal {
+
+///////////////////////////////////////////////////////////////////////
+//
+// PdbEngine
+//
+///////////////////////////////////////////////////////////////////////
+
+PdbEngine::PdbEngine(DebuggerManager *manager)
+    : IDebuggerEngine(manager)
+{}
+
+PdbEngine::~PdbEngine()
+{}
+
+void PdbEngine::executeDebuggerCommand(const QString &command)
+{
+    XSDEBUG("PdbEngine::executeDebuggerCommand:" << command);
+    if (state() == DebuggerNotReady) {
+        debugMessage(_("PDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
+        return;
+    }
+    m_pdbProc.write(command.toLatin1() + "\n");
+}
+
+void PdbEngine::postCommand(const QByteArray &command,
+//                 PdbCommandFlags flags,
+                 PdbCommandCallback callback,
+                 const char *callbackName,
+                 const QVariant &cookie)
+{
+    PdbCommand cmd;
+    cmd.command = command;
+    cmd.callback = callback;
+    cmd.callbackName = callbackName;
+    cmd.cookie = cookie;
+    m_commands.enqueue(cmd);
+    showDebuggerInput(LogMisc, _(cmd.command));
+    m_pdbProc.write(cmd.command + "\n");
+}
+
+void PdbEngine::shutdown()
+{
+    exitDebugger();
+}
+
+void PdbEngine::exitDebugger()
+{
+    if (state() == DebuggerNotReady)
+        return;
+    SDEBUG("PdbEngine::exitDebugger()");
+    m_pdbProc.kill();
+    //if (m_scriptEngine->isEvaluating())
+    //    m_scriptEngine->abortEvaluation();
+    setState(InferiorShuttingDown);
+    setState(InferiorShutDown);
+    setState(EngineShuttingDown);
+    //m_scriptEngine->setAgent(0);
+    setState(DebuggerNotReady);
+}
+
+void PdbEngine::startDebugger(const DebuggerStartParametersPtr &sp)
+{
+    setState(AdapterStarting);
+
+    m_scriptFileName = QFileInfo(sp->executable).absoluteFilePath();
+    QFile scriptFile(m_scriptFileName);
+    if (!scriptFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
+        //debugMessage("STARTING " +m_scriptFileName + "FAILED");
+        manager()->showDebuggerOutput(LogError, QString::fromLatin1("Cannot open %1: %2").
+                         arg(m_scriptFileName, scriptFile.errorString()));
+        emit startFailed();
+        return;
+    }
+    setState(AdapterStarted);
+    setState(InferiorStarting);
+    setState(InferiorRunningRequested);
+    showStatusMessage(tr("Running requested..."), 5000);
+
+    m_pdbProc.disconnect(); // From any previous runs
+
+    m_pdb = _("/usr/bin/python");
+    debugMessage(_("STARTING PDB ") + m_pdb);
+    QStringList gdbArgs;
+    gdbArgs += _("-i");
+    gdbArgs += _("/usr/bin/pdb");
+    gdbArgs += m_scriptFileName;
+    //gdbArgs += args;
+
+    connect(&m_pdbProc, SIGNAL(error(QProcess::ProcessError)),
+        SLOT(handlePdbError(QProcess::ProcessError)));
+    connect(&m_pdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
+        SLOT(handlePdbFinished(int, QProcess::ExitStatus)));
+    connect(&m_pdbProc, SIGNAL(readyReadStandardOutput()),
+        SLOT(readPdbStandardOutput()));
+    connect(&m_pdbProc, SIGNAL(readyReadStandardError()),
+        SLOT(readPdbStandardError()));
+
+    // We will stop immediatly, so setup a proper callback.
+    PdbCommand cmd;
+    cmd.callback = &PdbEngine::handleUpdateAll;
+    m_commands.enqueue(cmd);
+
+    m_pdbProc.start(m_pdb, gdbArgs);
+    qDebug() << "STARTING:" << m_pdb << gdbArgs;
+
+    if (!m_pdbProc.waitForStarted()) {
+        const QString msg = tr("Unable to start pdb '%1': %2")
+            .arg(m_pdb, m_pdbProc.errorString());
+        setState(AdapterStartFailed);
+        debugMessage(_("ADAPTER START FAILED"));
+        if (!msg.isEmpty()) {
+            const QString title = tr("Adapter start failed");
+            Core::ICore::instance()->showWarningWithOptions(title, msg);
+        }
+        shutdown();
+        emit startFailed();
+        return;
+    }
+
+    emit startSuccessful();
+    setState(InferiorRunning);
+    attemptBreakpointSynchronization();
+
+    debugMessage(_("PDB STARTED, INITIALIZING IT"));
+    const QByteArray dumperSourcePath =
+        Core::ICore::instance()->resourcePath().toLocal8Bit() + "/gdbmacros/";
+    postCommand("execfile('" + dumperSourcePath + "pdumper.py')",
+        CB(handleLoadDumper));
+}
+
+void PdbEngine::runInferior()
+{
+    SDEBUG("PdbEngine::runInferior()");
+    // FIXME: setState(InferiorRunning);
+}
+
+void PdbEngine::interruptInferior()
+{
+    setState(InferiorStopped);
+}
+
+void PdbEngine::executeStep()
+{
+    m_manager->resetLocation();
+    setState(InferiorRunningRequested);
+    setState(InferiorRunning);
+    postCommand("step", CB(handleUpdateAll));
+}
+
+void PdbEngine::executeStepI()
+{
+    m_manager->resetLocation();
+    setState(InferiorRunningRequested);
+    setState(InferiorRunning);
+    postCommand("step", CB(handleUpdateAll));
+}
+
+void PdbEngine::executeStepOut()
+{
+    m_manager->resetLocation();
+    setState(InferiorRunningRequested);
+    setState(InferiorRunning);
+    postCommand("finish", CB(handleUpdateAll));
+}
+
+void PdbEngine::executeNext()
+{
+    m_manager->resetLocation();
+    setState(InferiorRunningRequested);
+    setState(InferiorRunning);
+    postCommand("next", CB(handleUpdateAll));
+}
+
+void PdbEngine::executeNextI()
+{
+    m_manager->resetLocation();
+    setState(InferiorRunningRequested);
+    setState(InferiorRunning);
+    postCommand("next", CB(handleUpdateAll));
+}
+
+void PdbEngine::continueInferior()
+{
+    m_manager->resetLocation();
+    setState(InferiorRunningRequested);
+    setState(InferiorRunning);
+    // Callback will be triggered e.g. when breakpoint is hit.
+    postCommand("continue", CB(handleUpdateAll));
+}
+
+void PdbEngine::executeRunToLine(const QString &fileName, int lineNumber)
+{
+    Q_UNUSED(fileName)
+    Q_UNUSED(lineNumber)
+    SDEBUG("FIXME:  PdbEngine::runToLineExec()");
+}
+
+void PdbEngine::executeRunToFunction(const QString &functionName)
+{
+    Q_UNUSED(functionName)
+    XSDEBUG("FIXME:  PdbEngine::runToFunctionExec()");
+}
+
+void PdbEngine::executeJumpToLine(const QString &fileName, int lineNumber)
+{
+    Q_UNUSED(fileName)
+    Q_UNUSED(lineNumber)
+    XSDEBUG("FIXME:  PdbEngine::jumpToLineExec()");
+}
+
+void PdbEngine::activateFrame(int frameIndex)
+{
+    manager()->resetLocation();
+    if (state() != InferiorStopped && state() != InferiorUnrunnable)
+        return;
+
+    StackHandler *stackHandler = manager()->stackHandler();
+    int oldIndex = stackHandler->currentIndex();
+
+    //if (frameIndex == stackHandler->stackSize()) {
+    //    reloadFullStack();
+    //    return;
+    //}
+
+    QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
+
+    if (oldIndex != frameIndex) {
+        // Assuming the command always succeeds this saves a roundtrip.
+        // Otherwise the lines below would need to get triggered
+        // after a response to this -stack-select-frame here.
+        stackHandler->setCurrentIndex(frameIndex);
+        //postCommand("-stack-select-frame " + QByteArray::number(frameIndex),
+        //    CB(handleStackSelectFrame));
+    }
+    manager()->gotoLocation(stackHandler->currentFrame(), true);
+}
+
+void PdbEngine::selectThread(int index)
+{
+    Q_UNUSED(index)
+}
+
+static QByteArray breakpointLocation(const BreakpointData *data)
+{
+    if (!data->funcName.isEmpty())
+        return data->funcName.toLatin1();
+    // In this case, data->funcName is something like '*0xdeadbeef'
+    if (data->lineNumber.toInt() == 0)
+        return data->funcName.toLatin1();
+    //QString loc = data->useFullPath ? data->fileName : breakLocation(data->fileName);
+    // The argument is simply a C-quoted version of the argument to the
+    // non-MI "break" command, including the "original" quoting it wants.
+    //return "\"\\\"" + GdbMi::escapeCString(data->fileName).toLocal8Bit() + "\\\":"
+    //    + data->lineNumber + '"';
+    return data->fileName.toLocal8Bit() + ":" + data->lineNumber;
+}
+
+void PdbEngine::attemptBreakpointSynchronization()
+{
+    BreakHandler *handler = manager()->breakHandler();
+    //qDebug() << "ATTEMPT BP SYNC";
+    bool updateNeeded = false;
+    for (int index = 0; index != handler->size(); ++index) {
+        BreakpointData *data = handler->at(index);
+        if (data->pending) {
+            data->pending = false; // FIXME
+            updateNeeded = true;
+            QByteArray loc = breakpointLocation(data);
+            postCommand("break " + loc, CB(handleBreakInsert), QVariant(index));
+        }
+/*
+        if (data->bpNumber.isEmpty()) {
+            data->bpNumber = QByteArray::number(index + 1);
+            updateNeeded = true;
+        }
+        if (!data->fileName.isEmpty() && data->markerFileName().isEmpty()) {
+            data->setMarkerFileName(data->fileName);
+            data->setMarkerLineNumber(data->lineNumber.toInt());
+            updateNeeded = true;
+        }
+*/
+    }
+    //if (updateNeeded)
+    //    handler->updateMarkers();
+}
+
+void PdbEngine::handleBreakInsert(const PdbResponse &response)
+{
+    //qDebug() << "BP RESPONSE: " << response.data;
+    // "Breakpoint 1 at /pdb/math.py:10"
+    int index = response.cookie.toInt();
+    BreakHandler *handler = manager()->breakHandler();
+    BreakpointData *data = handler->at(index);
+    QTC_ASSERT(data, return);
+    QTC_ASSERT(response.data.startsWith("Breakpoint "), return);
+    int pos1 = response.data.indexOf(" at ");
+    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);
+    data->bpNumber = bpnr;
+    data->bpFileName = _(file);
+    data->bpLineNumber = line;
+    handler->updateMarkers();
+}
+
+void PdbEngine::loadSymbols(const QString &moduleName)
+{
+    Q_UNUSED(moduleName)
+}
+
+void PdbEngine::loadAllSymbols()
+{
+}
+
+void PdbEngine::reloadModules()
+{
+}
+
+QList<Symbol> PdbEngine::moduleSymbols(const QString & /*moduleName*/)
+{
+    return QList<Symbol>();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Tooltip specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+
+static WatchData m_toolTip;
+static QPoint m_toolTipPos;
+static QHash<QString, WatchData> m_toolTipCache;
+
+void PdbEngine::setToolTipExpression(const QPoint &mousePos,
+    TextEditor::ITextEditor *editor, int cursorPos)
+{
+    Q_UNUSED(mousePos)
+    Q_UNUSED(editor)
+    Q_UNUSED(cursorPos)
+
+    if (state() != InferiorStopped) {
+        //SDEBUG("SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED");
+        return;
+    }
+    // Check mime type and get expression (borrowing some C++ - functions)
+    const QString javaPythonMimeType =
+        QLatin1String("application/javascript");
+    if (!editor->file() || editor->file()->mimeType() != javaPythonMimeType)
+        return;
+
+    int line;
+    int column;
+    QString exp = cppExpressionAt(editor, cursorPos, &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;
+    }
+
+    if (!hasLetterOrNumber(exp)) {
+        QToolTip::showText(m_toolTipPos, tr("'%1' contains no identifier").arg(exp));
+        return;
+    }
+
+    if (exp.startsWith(QLatin1Char('"')) && exp.endsWith(QLatin1Char('"'))) {
+        QToolTip::showText(m_toolTipPos, tr("String literal %1").arg(exp));
+        return;
+    }
+
+    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;
+
+    if (hasSideEffects(exp)) {
+        QToolTip::showText(m_toolTipPos,
+            tr("Cowardly refusing to evaluate expression '%1' "
+               "with potential side effects").arg(exp));
+        return;
+    }
+
+#if 0
+    //if (m_manager->status() != InferiorStopped)
+    //    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
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Watch specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void PdbEngine::assignValueInDebugger(const QString &expression,
+    const QString &value)
+{
+    Q_UNUSED(expression);
+    Q_UNUSED(value);
+    SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value));
+#if 0
+    m_scriptEngine->evaluate(expression + QLatin1Char('=') + value);
+    updateLocals();
+#endif
+}
+
+
+void PdbEngine::updateWatchData(const WatchData &data)
+{
+    Q_UNUSED(data);
+    updateAll();
+}
+
+void PdbEngine::handlePdbError(QProcess::ProcessError error)
+{
+    debugMessage(_("HANDLE PDB 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:
+        m_pdbProc.kill();
+        setState(EngineShuttingDown, true);
+        m_manager->showMessageBox(QMessageBox::Critical, tr("Pdb I/O Error"),
+                       errorMessage(error));
+        break;
+    }
+}
+
+QString PdbEngine::errorMessage(QProcess::ProcessError error) const
+{
+    switch (error) {
+        case QProcess::FailedToStart:
+            return tr("The Pdb process failed to start. Either the "
+                "invoked program '%1' is missing, or you may have insufficient "
+                "permissions to invoke the program.")
+                .arg(m_pdb);
+        case QProcess::Crashed:
+            return tr("The Pdb 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 Pdb 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 Pdb process. For example, the process may not be running.");
+        default:
+            return tr("An unknown error in the Pdb process occurred. ");
+    }
+}
+
+void PdbEngine::handlePdbFinished(int code, QProcess::ExitStatus type)
+{
+    debugMessage(_("PDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code));
+    //shutdown();
+    //initializeVariables();
+    setState(DebuggerNotReady, true);
+}
+
+void PdbEngine::readPdbStandardError()
+{
+    QByteArray err = m_pdbProc.readAllStandardError();
+    qWarning() << "Unexpected pdb stderr:" << err;
+    showDebuggerOutput(LogDebug, _("Unexpected pdb stderr: " + err));
+}
+
+void PdbEngine::readPdbStandardOutput()
+{
+    m_inbuffer.append(m_pdbProc.readAllStandardOutput());
+    //qDebug() << "BUFFER FROM: '" << m_inbuffer << "'";
+    int pos = 1;
+    while ((pos = m_inbuffer.indexOf("(Pdb)")) != -1) {
+        PdbResponse response;
+        response.data = m_inbuffer.left(pos).trimmed();
+        showDebuggerOutput(LogDebug, _(response.data));
+        m_inbuffer = m_inbuffer.mid(pos + 6);
+        QTC_ASSERT(!m_commands.isEmpty(),
+            qDebug() << "RESPONSE: " << response.data; return)
+        PdbCommand cmd = m_commands.dequeue();
+        response.cookie = cmd.cookie;
+        if (cmd.callback) {
+            //qDebug() << "EXECUTING CALLBACK " << cmd.callbackName
+            //    << " RESPONSE: " << response.data;
+            (this->*cmd.callback)(response);
+        } else {
+            qDebug() << "NO CALLBACK FOR RESPONSE: " << response.data;
+        }
+    }
+    //qDebug() << "BUFFER LEFT: '" << m_inbuffer << "'";
+}
+
+void PdbEngine::handleResponse(const QByteArray &response0)
+{
+    QByteArray response = response0;
+    qDebug() << "RESPONSE: '" << response << "'";
+    if (response.startsWith("--Call--")) {
+        qDebug() << "SKIPPING '--Call--' MARKER";
+        response = response.mid(9);
+    }
+    if (response.startsWith("--Return--")) {
+        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()) {
+                manager()->gotoLocation(frame, true);
+                setState(InferiorStopping);
+                setState(InferiorStopped);
+                return;
+            }
+        }
+    }
+    qDebug() << "COULD NOT PARSE RESPONSE: '" << response << "'";
+}
+
+void PdbEngine::handleUpdateAll(const PdbResponse &response)
+{
+    Q_UNUSED(response);
+    updateAll();
+}
+
+void PdbEngine::updateAll()
+{
+    setState(InferiorStopping);
+    setState(InferiorStopped);
+
+    WatchHandler *handler = m_manager->watchHandler();
+
+    QByteArray watchers;
+    //if (!m_toolTipExpression.isEmpty())
+    //    watchers += m_toolTipExpression.toLatin1()
+    //        + '#' + tooltipINameForExpression(m_toolTipExpression.toLatin1());
+
+    QHash<QByteArray, int> watcherNames = handler->watcherNames();
+    QHashIterator<QByteArray, int> it(watcherNames);
+    while (it.hasNext()) {
+        it.next();
+        if (!watchers.isEmpty())
+            watchers += "##";
+        if (it.key() == WatchHandler::watcherEditPlaceHolder().toLatin1())
+            watchers += "<Edit>#watch." + QByteArray::number(it.value());
+        else
+            watchers += it.key() + "#watch." + QByteArray::number(it.value());
+    }
+
+    QByteArray options;
+    if (theDebuggerBoolSetting(UseDebuggingHelpers))
+        options += "fancy,";
+    if (theDebuggerBoolSetting(AutoDerefPointers))
+        options += "autoderef,";
+    if (options.isEmpty())
+        options += "defaults,";
+    options.chop(1);
+
+    postCommand("bt", CB(handleBacktrace));
+    postCommand("qdebug('" + options + "','"
+        + handler->expansionRequests() + "','"
+        + handler->typeFormatRequests() + "','"
+        + handler->individualFormatRequests() + "','"
+        + watchers.toHex() + "')", CB(handleListLocals));
+}
+
+void PdbEngine::handleBacktrace(const PdbResponse &response)
+{
+    //qDebug() << " BACKTRACE: '" << response.data << "'";
+    // "  /usr/lib/python2.6/bdb.py(368)run()"
+    // "-> exec cmd in globals, locals"
+    // "  <string>(1)<module>()"
+    // "  /python/math.py(19)<module>()"
+    // "-> 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.
+    QList<StackFrame> 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));
+                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; 
+    manager()->stackHandler()->setFrames(stackFrames);
+
+    // Select current frame.
+    if (currentIndex != -1) {
+        currentIndex = frameCount - currentIndex - 1;
+        manager()->stackHandler()->setCurrentIndex(currentIndex);
+        manager()->gotoLocation(stackFrames.at(currentIndex), true);
+    }
+}
+
+void PdbEngine::handleListLocals(const PdbResponse &response)
+{
+    //qDebug() << " LOCALS: '" << response.data << "'";
+    QByteArray out = response.data.trimmed();
+
+    GdbMi all;
+    all.fromStringMultiple(out);
+    //qDebug() << "ALL: " << all.toString();
+
+    //GdbMi data = all.findChild("data");
+    QList<WatchData> list;
+    WatchHandler *watchHandler = manager()->watchHandler();
+    foreach (const GdbMi &child, all.children()) {
+        WatchData dummy;
+        dummy.iname = child.findChild("iname").data();
+        dummy.name = _(child.findChild("name").data());
+        //qDebug() << "CHILD: " << child.toString();
+        parseWatchData(watchHandler->expandedINames(), dummy, child, &list);
+    }
+    watchHandler->insertBulkData(list);
+}
+
+void PdbEngine::handleLoadDumper(const PdbResponse &response)
+{
+    Q_UNUSED(response);
+    //qDebug() << " DUMPERS LOADED '" << response.data << "'";
+    continueInferior();
+}
+
+void PdbEngine::debugMessage(const QString &msg)
+{
+    showDebuggerOutput(LogDebug, msg);
+}
+
+IDebuggerEngine *createPdbEngine(DebuggerManager *manager)
+{
+    return new PdbEngine(manager);
+}
+
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h
new file mode 100644
index 00000000000..2b1f9d12ffd
--- /dev/null
+++ b/src/plugins/debugger/pdb/pdbengine.h
@@ -0,0 +1,160 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef DEBUGGER_PDBENGINE_H
+#define DEBUGGER_PDBENGINE_H
+
+#include "idebuggerengine.h"
+
+#include <QtCore/QProcess>
+#include <QtCore/QQueue>
+#include <QtCore/QVariant>
+
+
+namespace Debugger {
+namespace Internal {
+
+class WatchData;
+class GdbMi;
+
+/* A debugger engine for Python using the pdb command line debugger.
+ */
+
+class PdbResponse
+{
+public:
+    QByteArray data;
+    QVariant cookie;
+};
+
+class PdbEngine : public IDebuggerEngine
+{
+    Q_OBJECT
+
+public:
+    PdbEngine(DebuggerManager *manager);
+    ~PdbEngine();
+
+private:
+    // IDebuggerEngine implementation
+    void executeStep();
+    void executeStepOut();
+    void executeNext();
+    void executeStepI();
+    void executeNextI();
+
+    void shutdown();
+    void setToolTipExpression(const QPoint &mousePos,
+        TextEditor::ITextEditor *editor, int cursorPos);
+    void startDebugger(const DebuggerStartParametersPtr &sp);
+
+    void exitDebugger();
+
+    void continueInferior();
+    Q_SLOT void runInferior();
+    void interruptInferior();
+
+    void executeRunToLine(const QString &fileName, int lineNumber);
+    void executeRunToFunction(const QString &functionName);
+    void executeJumpToLine(const QString &fileName, int lineNumber);
+
+    void activateFrame(int index);
+    void selectThread(int index);
+
+    void attemptBreakpointSynchronization();
+
+    void assignValueInDebugger(const QString &expr, const QString &value);
+    void executeDebuggerCommand(const QString & command);
+
+    void loadSymbols(const QString &moduleName);
+    void loadAllSymbols();
+    virtual QList<Symbol> moduleSymbols(const QString &moduleName);
+    void reloadModules();
+    void reloadRegisters() {}
+    void reloadSourceFiles() {}
+    void reloadFullStack() {}
+
+    bool supportsThreads() const { return true; }
+    bool isSynchroneous() const { return true; }
+    void updateWatchData(const WatchData &data);
+
+private:
+    void debugMessage(const QString &msg);
+    QString errorMessage(QProcess::ProcessError error) const;
+
+    Q_SLOT void handlePdbFinished(int, QProcess::ExitStatus status);
+    Q_SLOT void handlePdbError(QProcess::ProcessError error);
+    Q_SLOT void readPdbStandardOutput();
+    Q_SLOT void readPdbStandardError();
+    void handleResponse(const QByteArray &ba);
+    void updateAll();
+    void handleUpdateAll(const PdbResponse &response);
+
+    typedef void (PdbEngine::*PdbCommandCallback)
+        (const PdbResponse &response);
+
+    struct PdbCommand
+    {
+        PdbCommand()
+            : callback(0), callbackName(0)
+        {}
+
+        PdbCommandCallback callback;
+        const char *callbackName;
+        QByteArray command;
+        QVariant cookie;
+        //QTime postTime;
+    };
+
+    void handleStop(const PdbResponse &response);
+    void handleBacktrace(const PdbResponse &response);
+    void handleListLocals(const PdbResponse &response);
+    void handleLoadDumper(const PdbResponse &response);
+    void handleBreakInsert(const PdbResponse &response);
+
+    void handleChildren(const WatchData &data0, const GdbMi &item,
+        QList<WatchData> *list);
+    void postCommand(const QByteArray &command,
+                     //GdbCommandFlags flags = 0,
+                     PdbCommandCallback callback = 0,
+                     const char *callbackName = 0,
+                     const QVariant &cookie = QVariant());
+
+    QQueue<PdbCommand> m_commands;
+
+    QByteArray m_inbuffer; 
+    QString m_scriptFileName;
+    QProcess m_pdbProc;
+    QString m_pdb;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_PDBENGINE_H
diff --git a/src/plugins/debugger/script/scriptengine.cpp b/src/plugins/debugger/script/scriptengine.cpp
index d4588f6520f..8b0566a999d 100644
--- a/src/plugins/debugger/script/scriptengine.cpp
+++ b/src/plugins/debugger/script/scriptengine.cpp
@@ -550,7 +550,7 @@ void ScriptEngine::setToolTipExpression(const QPoint &mousePos,
 void ScriptEngine::assignValueInDebugger(const QString &expression,
     const QString &value)
 {
-    XSDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value));
+    SDEBUG("ASSIGNING: " << (expression + QLatin1Char('=') + value));
     m_scriptEngine->evaluate(expression + QLatin1Char('=') + value);
     updateLocals();
 }
diff --git a/tests/manual/gdbdebugger/python/math.py b/tests/manual/gdbdebugger/python/math.py
new file mode 100644
index 00000000000..5b8fbcce449
--- /dev/null
+++ b/tests/manual/gdbdebugger/python/math.py
@@ -0,0 +1,19 @@
+
+def square(a):
+    x = a * a
+    return a
+
+def cube(a):
+    x = square(a)
+    x = x * a
+    x = x + 1
+    x = x - 1
+    return x
+
+def main():
+    print cube(3)
+    print cube(4)
+    print cube(5)
+
+if __name__ == '__main__':
+  main()
diff --git a/tests/manual/gdbdebugger/python/python.pro b/tests/manual/gdbdebugger/python/python.pro
new file mode 100644
index 00000000000..c52f5b0e526
--- /dev/null
+++ b/tests/manual/gdbdebugger/python/python.pro
@@ -0,0 +1,4 @@
+
+TEMPLATE = script
+TARGET = math.py
+SOURCES += math.py
-- 
GitLab