Commit aa4485af authored by hjk's avatar hjk
Browse files

Debugger: Rework Python Debugger



The (re-)enables basic stepping, data display, frame selection etc
for Python 2 and 3. Arguments passing, jump to line etc.
don't work yet.

Change-Id: I8af03e5905092360eb268ba3081a1236b1f8577f
Reviewed-by: default avatarChristian Stenger <christian.stenger@theqtcompany.com>
parent 9c27b9fd
This diff is collapsed.
......@@ -110,6 +110,10 @@ public:
QString projectSourceDirectory;
QStringList projectSourceFiles;
// Used by Script debugging
QString interpreter;
QString mainScript;
// Used by AttachCrashedExternal.
QString crashParameter;
......
......@@ -147,7 +147,8 @@ void DebuggerRunControl::start()
QTC_ASSERT(m_engine, return);
// User canceled input dialog asking for executable when working on library project.
if (m_engine->runParameters().startMode == StartInternal
&& m_engine->runParameters().executable.isEmpty()) {
&& m_engine->runParameters().executable.isEmpty()
&& m_engine->runParameters().interpreter.isEmpty()) {
appendMessage(tr("No executable specified.") + QLatin1Char('\n'), ErrorMessageFormat);
emit started();
emit finished();
......@@ -438,6 +439,22 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
if (m_runConfig)
m_debuggerAspect = m_runConfig->extraAspect<DebuggerRunConfigurationAspect>();
if (m_rp.displayName.isEmpty() && m_runConfig)
m_rp.displayName = m_runConfig->displayName();
if (runConfig->property("supportsDebugger").toBool()) {
QString mainScript = runConfig->property("mainScript").toString();
QString interpreter = runConfig->property("interpreter").toString();
if (!interpreter.isEmpty() && mainScript.endsWith(_(".py"))) {
m_rp.mainScript = mainScript;
m_rp.interpreter = interpreter;
QString args = runConfig->property("arguments").toString();
if (!args.isEmpty())
QtcProcess::addArg(&m_rp.processArgs, args);
m_rp.masterEngineType = PdbEngineType;
}
}
if (m_debuggerAspect) {
m_rp.multiProcess = m_debuggerAspect->useMultiProcess();
......@@ -469,17 +486,6 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
}
}
if (m_rp.displayName.isEmpty() && m_runConfig)
m_rp.displayName = m_runConfig->displayName();
if (m_rp.masterEngineType == NoEngineType) {
if (m_rp.executable.endsWith(_(".py"))
|| m_rp.executable == _("/usr/bin/python")
|| m_rp.executable == _("/usr/bin/python3")) {
m_rp.masterEngineType = PdbEngineType;
}
}
if (!boolSetting(AutoEnrichParameters)) {
const QString sysroot = m_rp.sysRoot;
if (m_rp.debugInfoLocation.isEmpty())
......@@ -566,6 +572,12 @@ void DebuggerRunControlCreator::createRunControl(Core::Id runMode)
//
////////////////////////////////////////////////////////////////////////
static bool isDebuggableScript(RunConfiguration *runConfig)
{
QString mainScript = runConfig->property("mainScript").toString();
return mainScript.endsWith(_(".py")); // Only Python for now.
}
class DebuggerRunControlFactory : public IRunControlFactory
{
public:
......@@ -592,7 +604,8 @@ public:
bool canRun(RunConfiguration *runConfig, Core::Id mode) const override
{
return (mode == DebugRunMode || mode == DebugRunModeWithBreakOnMain)
&& qobject_cast<LocalApplicationRunConfiguration *>(runConfig);
&& (qobject_cast<LocalApplicationRunConfiguration *>(runConfig)
|| isDebuggableScript(runConfig));
}
IRunConfigurationAspect *createRunConfigurationAspect(RunConfiguration *rc) override
......
......@@ -49,6 +49,7 @@
#include <debugger/watchutils.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <coreplugin/idocument.h>
#include <coreplugin/icore.h>
......@@ -77,7 +78,6 @@ void PdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages
if (!(languages & CppLanguage))
return;
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
//XSDEBUG("PdbEngine::executeDebuggerCommand:" << command);
if (state() == DebuggerNotReady) {
showMessage(_("PDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
return;
......@@ -117,8 +117,8 @@ void PdbEngine::setupEngine()
{
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
QString python = pythonInterpreter();
showMessage(_("STARTING PDB ") + python);
m_interpreter = runParameters().interpreter;
QString bridge = ICore::resourcePath() + QLatin1String("/debugger/pdbbridge.py");
connect(&m_proc, static_cast<void(QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
this, &PdbEngine::handlePdbError);
......@@ -129,11 +129,22 @@ void PdbEngine::setupEngine()
connect(&m_proc, &QProcess::readyReadStandardError,
this, &PdbEngine::readPdbStandardError);
m_proc.start(python, QStringList() << _("-i"));
QFile scriptFile(runParameters().mainScript);
if (!scriptFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
AsynchronousMessageBox::critical(tr("Python Error"),
_("Cannot open script file %1:\n%2").
arg(scriptFile.fileName(), scriptFile.errorString()));
notifyEngineSetupFailed();
}
QStringList args = { bridge, scriptFile.fileName() };
args.append(Utils::QtcProcess::splitArgs(runParameters().processArgs));
showMessage(_("STARTING ") + m_interpreter + QLatin1Char(' ') + args.join(QLatin1Char(' ')));
m_proc.start(m_interpreter, args);
if (!m_proc.waitForStarted()) {
const QString msg = tr("Unable to start pdb \"%1\": %2")
.arg(pythonInterpreter(), m_proc.errorString());
.arg(m_interpreter, m_proc.errorString());
notifyEngineSetupFailed();
showMessage(_("ADAPTER START FAILED"));
if (!msg.isEmpty())
......@@ -148,43 +159,19 @@ void PdbEngine::setupInferior()
{
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
QString fileName = mainPythonFile();
QFile scriptFile(fileName);
if (!scriptFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
AsynchronousMessageBox::critical(tr("Python Error"),
_("Cannot open script file %1:\n%2").
arg(fileName, scriptFile.errorString()));
notifyInferiorSetupFailed();
return;
}
notifyInferiorSetupOk();
}
QString PdbEngine::mainPythonFile() const
{
return QFileInfo(runParameters().processArgs).absoluteFilePath();
}
QString PdbEngine::pythonInterpreter() const
{
return runParameters().executable;
}
void PdbEngine::runEngine()
{
QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
showStatusMessage(tr("Running requested..."), 5000);
QByteArray bridge = ICore::resourcePath().toUtf8() + "/debugger/pdbbridge.py";
QByteArray pdb = "/usr/bin/pdb";
if (pythonInterpreter().endsWith(QLatin1Char('3')))
pdb += '3';
postDirectCommand("import sys");
postDirectCommand("sys.argv.append('" + mainPythonFile().toLocal8Bit() + "')");
postDirectCommand("exec(open('" + pdb + "').read())");
postDirectCommand("exec(open('" + bridge + "').read())");
attemptBreakpointSynchronization();
notifyEngineRunAndInferiorStopOk();
continueInferior();
if (runParameters().breakOnMain)
updateAll();
else
continueInferior();
}
void PdbEngine::interruptInferior()
......@@ -198,6 +185,7 @@ void PdbEngine::executeStep()
notifyInferiorRunRequested();
notifyInferiorRunOk();
postDirectCommand("step");
updateAll();
}
void PdbEngine::executeStepI()
......@@ -206,6 +194,7 @@ void PdbEngine::executeStepI()
notifyInferiorRunRequested();
notifyInferiorRunOk();
postDirectCommand("step");
updateAll();
}
void PdbEngine::executeStepOut()
......@@ -214,6 +203,7 @@ void PdbEngine::executeStepOut()
notifyInferiorRunRequested();
notifyInferiorRunOk();
postDirectCommand("return");
updateAll();
}
void PdbEngine::executeNext()
......@@ -222,6 +212,7 @@ void PdbEngine::executeNext()
notifyInferiorRunRequested();
notifyInferiorRunOk();
postDirectCommand("next");
updateAll();
}
void PdbEngine::executeNextI()
......@@ -230,6 +221,7 @@ void PdbEngine::executeNextI()
notifyInferiorRunRequested();
notifyInferiorRunOk();
postDirectCommand("next");
updateAll();
}
void PdbEngine::continueInferior()
......@@ -239,6 +231,7 @@ void PdbEngine::continueInferior()
notifyInferiorRunOk();
// Callback will be triggered e.g. when breakpoint is hit.
postDirectCommand("continue");
updateAll();
}
void PdbEngine::executeRunToLine(const ContextData &data)
......@@ -261,28 +254,14 @@ void PdbEngine::executeJumpToLine(const ContextData &data)
void PdbEngine::activateFrame(int frameIndex)
{
resetLocation();
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
return;
StackHandler *handler = stackHandler();
int oldIndex = handler->currentIndex();
//if (frameIndex == handler->stackSize()) {
// reloadFullStack();
// return;
//}
QTC_ASSERT(frameIndex < handler->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.
handler->setCurrentIndex(frameIndex);
//postDirectCommand("-stack-select-frame " + QByteArray::number(frameIndex));
}
handler->setCurrentIndex(frameIndex);
gotoLocation(handler->currentFrame());
updateLocals();
}
void PdbEngine::selectThread(ThreadId threadId)
......@@ -402,7 +381,6 @@ void PdbEngine::updateItem(const QByteArray &iname)
void PdbEngine::handlePdbError(QProcess::ProcessError error)
{
qDebug() << "HANDLE PDB ERROR";
showMessage(_("HANDLE PDB ERROR"));
switch (error) {
case QProcess::Crashed:
......@@ -426,7 +404,7 @@ QString PdbEngine::errorMessage(QProcess::ProcessError error) const
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(pythonInterpreter());
.arg(m_interpreter);
case QProcess::Crashed:
return tr("The Pdb process crashed some time after starting "
"successfully.");
......@@ -448,7 +426,6 @@ QString PdbEngine::errorMessage(QProcess::ProcessError error) const
void PdbEngine::handlePdbFinished(int code, QProcess::ExitStatus type)
{
qDebug() << "PDB FINISHED";
showMessage(_("PDB PROCESS FINISHED, status %1, code %2").arg(type).arg(code));
notifyEngineSpontaneousShutdown();
}
......@@ -456,24 +433,20 @@ void PdbEngine::handlePdbFinished(int code, QProcess::ExitStatus type)
void PdbEngine::readPdbStandardError()
{
QByteArray err = m_proc.readAllStandardError();
qDebug() << "\nPDB STDERR" << err;
//qWarning() << "Unexpected pdb stderr:" << err;
//showMessage(_("Unexpected pdb stderr: " + err));
showMessage(_("Unexpected pdb stderr: " + err));
//handleOutput(err);
}
void PdbEngine::readPdbStandardOutput()
{
QByteArray out = m_proc.readAllStandardOutput();
qDebug() << "\nPDB STDOUT" << out;
handleOutput(out);
}
void PdbEngine::handleOutput(const QByteArray &data)
{
//qDebug() << "READ: " << data;
m_inbuffer.append(data);
qDebug() << "BUFFER FROM: '" << m_inbuffer << '\'';
while (true) {
int pos = m_inbuffer.indexOf("(Pdb)");
if (pos == -1)
......@@ -484,8 +457,6 @@ void PdbEngine::handleOutput(const QByteArray &data)
m_inbuffer = m_inbuffer.mid(pos + 6);
handleOutput2(response);
}
qDebug() << "BUFFER LEFT: '" << m_inbuffer << '\'';
//m_inbuffer.clear();
}
void PdbEngine::handleOutput2(const QByteArray &data)
......@@ -532,8 +503,6 @@ void PdbEngine::handleOutput2(const QByteArray &data)
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;
......@@ -546,27 +515,10 @@ void PdbEngine::handleOutput2(const QByteArray &data)
}
}
}
showMessage(_(" #### ... UNHANDLED"));
}
}
}
/*
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);
}
}
*/
void PdbEngine::refreshLocals(const GdbMi &vars)
{
WatchHandler *handler = watchHandler();
......@@ -657,6 +609,7 @@ void PdbEngine::updateLocals()
//cmd.arg("resultvarname", m_resultVarName);
//m_lastDebuggableCommand = cmd;
//m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1");
cmd.arg("frame", stackHandler()->currentIndex());
watchHandler()->notifyUpdateStarted();
runCommand(cmd);
......
......@@ -98,9 +98,6 @@ private:
bool isSynchronous() const { return true; }
void updateItem(const QByteArray &iname);
QString mainPythonFile() const;
QString pythonInterpreter() const;
void runCommand(const DebuggerCommand &cmd);
void postDirectCommand(const QByteArray &command);
......@@ -124,6 +121,7 @@ private:
QByteArray m_inbuffer;
QProcess m_proc;
QString m_interpreter;
};
} // namespace Internal
......
......@@ -232,8 +232,8 @@ class PythonProjectManager : public IProjectManager
public:
PythonProjectManager() {}
QString mimeType() const { return QLatin1String(PythonMimeType); }
Project *openProject(const QString &fileName, QString *errorString);
QString mimeType() const override { return QLatin1String(PythonMimeType); }
Project *openProject(const QString &fileName, QString *errorString) override;
void registerProject(PythonProject *project) { m_projects.append(project); }
void unregisterProject(PythonProject *project) { m_projects.removeAll(project); }
......@@ -248,12 +248,12 @@ public:
PythonProject(PythonProjectManager *manager, const QString &filename);
~PythonProject();
QString displayName() const { return m_projectName; }
IDocument *document() const;
IProjectManager *projectManager() const { return m_manager; }
QString displayName() const override { return m_projectName; }
IDocument *document() const override;
IProjectManager *projectManager() const override { return m_manager; }
ProjectNode *rootProjectNode() const;
QStringList files(FilesMode) const { return m_files; }
ProjectNode *rootProjectNode() const override;
QStringList files(FilesMode) const override { return m_files; }
QStringList files() const { return m_files; }
bool addFiles(const QStringList &filePaths);
......@@ -262,10 +262,9 @@ public:
bool renameFile(const QString &filePath, const QString &newFilePath);
void refresh();
protected:
bool fromMap(const QVariantMap &map);
private:
RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override;
bool saveRawFileList(const QStringList &rawFileList);
bool saveRawList(const QStringList &rawList, const QString &fileName);
void parseProject();
......@@ -295,7 +294,7 @@ public:
setFilePath(FileName::fromString(fileName));
}
bool save(QString *errorString, const QString &fileName, bool autoSave)
bool save(QString *errorString, const QString &fileName, bool autoSave) override
{
Q_UNUSED(errorString)
Q_UNUSED(fileName)
......@@ -303,20 +302,20 @@ public:
return false;
}
QString defaultPath() const { return QString(); }
QString suggestedFileName() const { return QString(); }
QString defaultPath() const override { return QString(); }
QString suggestedFileName() const override { return QString(); }
bool isModified() const { return false; }
bool isSaveAsAllowed() const { return false; }
bool isModified() const override { return false; }
bool isSaveAsAllowed() const override { return false; }
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override
{
Q_UNUSED(state)
Q_UNUSED(type)
return BehaviorSilent;
}
bool reload(QString *errorString, ReloadFlag flag, ChangeType type)
bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override
{
Q_UNUSED(errorString)
Q_UNUSED(flag)
......@@ -338,19 +337,19 @@ public:
Core::IDocument *projectFile() const;
QString projectFilePath() const;
bool showInSimpleTree() const;
bool showInSimpleTree() const override;
QList<ProjectAction> supportedActions(Node *node) const;
QList<ProjectAction> supportedActions(Node *node) const override;
bool canAddSubProject(const QString &proFilePath) const;
bool canAddSubProject(const QString &proFilePath) const override;
bool addSubProjects(const QStringList &proFilePaths);
bool removeSubProjects(const QStringList &proFilePaths);
bool addSubProjects(const QStringList &proFilePaths) override;
bool removeSubProjects(const QStringList &proFilePaths) override;
bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0);
bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0);
bool deleteFiles(const QStringList &filePaths);
bool renameFile(const QString &filePath, const QString &newFilePath);
bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override;
bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0) override;
bool deleteFiles(const QStringList &filePaths) override;
bool renameFile(const QString &filePath, const QString &newFilePath) override;
void refresh(QSet<QString> oldFileList = QSet<QString>());
......@@ -379,26 +378,34 @@ private:
QLabel *m_scriptLabel;
};
class PythonRunConfiguration : public RunConfiguration
class PythonRunConfiguration : public ProjectExplorer::RunConfiguration
{
Q_OBJECT
Q_PROPERTY(bool supportsDebugger READ supportsDebugger)
Q_PROPERTY(QString interpreter READ interpreter)
Q_PROPERTY(QString mainScript READ mainScript)
Q_PROPERTY(QString arguments READ arguments)
public:
PythonRunConfiguration(Target *parent, Core::Id id);
PythonRunConfiguration(ProjectExplorer::Target *parent, Core::Id id);
QWidget *createConfigurationWidget();
QVariantMap toMap() const;
bool fromMap(const QVariantMap &map);
bool isEnabled() const { return m_enabled; }
QString disabledReason() const;
QWidget *createConfigurationWidget() override;
QVariantMap toMap() const override;
bool fromMap(const QVariantMap &map) override;
bool isEnabled() const override { return m_enabled; }
QString disabledReason() const override;
bool supportsDebugger() const { return true; }
QString mainScript() const { return m_mainScript; }
QString arguments() const;
QString interpreter() const { return m_interpreter; }
void setInterpreter(const QString &interpreter) { m_interpreter = interpreter; }
void setEnabled(bool b);
private:
friend class PythonRunConfigurationFactory;
PythonRunConfiguration(Target *parent, PythonRunConfiguration *source);
PythonRunConfiguration(ProjectExplorer::Target *parent, PythonRunConfiguration *source);
QString defaultDisplayName() const;
QString m_interpreter;
......@@ -412,9 +419,9 @@ class PythonRunControl : public RunControl
public:
PythonRunControl(PythonRunConfiguration *runConfiguration, Core::Id mode);
void start();
StopResult stop();
bool isRunning() const { return m_running; }
void start() override;
StopResult stop() override;
bool isRunning() const override { return m_running; }
private:
void processStarted();
......@@ -501,6 +508,13 @@ QString PythonRunConfiguration::disabledReason() const
return QString();
}
QString PythonRunConfiguration::arguments() const
{
auto aspect = extraAspect<ArgumentsAspect>();
QTC_ASSERT(aspect, return QString());
return aspect->arguments();
}
PythonRunConfigurationWidget::PythonRunConfigurationWidget(PythonRunConfiguration *runConfiguration, QWidget *parent)
: QWidget(parent), m_runConfiguration(runConfiguration)
{
......@@ -555,7 +569,7 @@ public:
setObjectName(QLatin1String("PythonRunConfigurationFactory"));
}
QList<Core::Id> availableCreationIds(Target *parent, CreationMode mode) const
QList<Core::Id> availableCreationIds(Target *parent, CreationMode mode) const override
{
Q_UNUSED(mode);
if (!canHandle(parent))
......@@ -569,12 +583,12 @@ public:
return allIds;
}
QString displayNameForId(Core::Id id) const
QString displayNameForId(Core::Id id) const override
{
return scriptFromId(id);
}
bool canCreate(Target *parent, Core::Id id) const
bool canCreate(Target *parent, Core::Id id) const override
{
if (!canHandle(parent))
return false;
......@@ -582,20 +596,20 @@ public:
return project->files().contains(scriptFromId(id));
}
bool canRestore(Target *parent, const QVariantMap &map) const
bool canRestore(Target *parent, const QVariantMap &map) const override
{
Q_UNUSED(parent);
return idFromMap(map).name().startsWith(PythonRunConfigurationPrefix);
}
bool canClone(Target *parent, RunConfiguration *source) const
bool canClone(Target *parent, RunConfiguration *source) const override
{
if (!canHandle(parent))
return false;
return source->id().name().startsWith(PythonRunConfigurationPrefix);
}
RunConfiguration *clone(Target *parent, RunConfiguration *source)
RunConfiguration *clone(Target *parent, RunConfiguration *source) override
{
if (!canClone(parent, source))
return 0;
......@@ -605,12 +619,12 @@ public:
private:
bool canHandle(Target *parent) const { return dynamic_cast<PythonProject *>(parent->project()); }
RunConfiguration *doCreate(Target *parent, Core::Id id)