Commit 355f44fe authored by Friedemann Kleint's avatar Friedemann Kleint Committed by unknown
Browse files

Fixes: Error logging, polish attach remote dialog

parent cb2cce51
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AttachRemoteDialog</class>
<widget class="QDialog" name="AttachRemoteDialog" >
<property name="geometry" >
<widget class="QDialog" name="AttachRemoteDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
......@@ -9,50 +10,76 @@
<height>866</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Start Debugger</string>
</property>
<layout class="QVBoxLayout" >
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" >
<property name="margin">
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QLabel" name="pidLabel" >
<property name="text" >
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="pidLabel">
<property name="text">
<string>Attach to Process ID:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="pidLineEdit" />
<item row="0" column="1">
<widget class="QLineEdit" name="pidLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Filter:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QWidget" name="filterWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="filterLineEdit"/>
</item>
<item>
<widget class="QToolButton" name="filterClearToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="procView" >
<property name="editTriggers" >
<widget class="QTreeView" name="procView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line" >
<property name="orientation" >
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
......
......@@ -33,6 +33,7 @@
#include "debuggermanager.h"
#include "breakhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
#include <utils/qtcassert.h>
#include <utils/winutils.h>
......@@ -50,11 +51,13 @@
static const char *dbgEngineDllC = "dbgeng";
static const char *debugCreateFuncC = "DebugCreate";
static QString debugEngineComError(HRESULT hr)
static QString msgDebugEngineComResult(HRESULT hr)
{
if (!FAILED(hr))
return QLatin1String("S_OK");
switch (hr) {
case S_OK:
return QLatin1String("S_OK");
case S_FALSE:
return QLatin1String("S_FALSE");
case E_FAIL:
break;
case E_INVALIDARG:
......@@ -207,6 +210,7 @@ bool CdbDebugEngine::startDebugger()
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage;
bool rc = false;
m_d->m_bIgnoreNextDebugEvent = false;
switch (m_d->m_debuggerManager->startMode()) {
case AttachExternal:
rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage);
......@@ -235,7 +239,7 @@ bool CdbDebugEngine::startAttachDebugger(unsigned long pid, QString *errorMessag
if (debugCDB)
qDebug() << "Attaching to " << pid << " returns " << hr;
if (FAILED(hr)) {
*errorMessage = tr("AttachProcess failed for pid %1: %2").arg(pid).arg(debugEngineComError(hr));
*errorMessage = tr("AttachProcess failed for pid %1: %2").arg(pid).arg(msgDebugEngineComResult(hr));
return false;
}
return true;
......@@ -276,7 +280,7 @@ bool CdbDebugEngine::startDebuggerWithExecutable(QString *errorMessage)
m_d->m_debuggerManager->m_workingDir.utf16(),
env);
if (FAILED(hr)) {
*errorMessage = tr("CreateProcess2Wide failed for '%1': %2").arg(cmd).arg(debugEngineComError(hr));
*errorMessage = tr("CreateProcess2Wide failed for '%1': %2").arg(cmd).arg(msgDebugEngineComResult(hr));
m_d->m_debuggerManagerAccess->notifyInferiorExited();
return false;
}
......@@ -295,6 +299,9 @@ void CdbDebugEngine::exitDebugger()
void CdbDebugEngine::updateWatchModel()
{
const QList<WatchData> incomplete = m_d->m_debuggerManagerAccess->watchHandler()->takeCurrentIncompletes();
if (debugCDB)
qDebug() << Q_FUNC_INFO << incomplete.size();
}
void CdbDebugEngine::stepExec()
......@@ -357,9 +364,12 @@ void CdbDebugEngine::nextExec()
if (debugCDB)
qDebug() << Q_FUNC_INFO;
HRESULT hr;
hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER);
startWatchTimer();
const HRESULT hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER);
if (SUCCEEDED(hr)) {
startWatchTimer();
} else {
qWarning("%s failed: %s", Q_FUNC_INFO, qPrintable(msgDebugEngineComResult(hr)));
}
}
void CdbDebugEngine::stepIExec()
......@@ -372,8 +382,12 @@ void CdbDebugEngine::nextIExec()
if (debugCDB)
qDebug() << Q_FUNC_INFO;
m_d->m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
startWatchTimer();
const HRESULT hr = m_d->m_pDebugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0);
if (SUCCEEDED(hr)) {
startWatchTimer();
} else {
qWarning("%s failed: %s", Q_FUNC_INFO, qPrintable(msgDebugEngineComResult(hr)));
}
}
void CdbDebugEngine::continueInferior()
......@@ -386,26 +400,30 @@ void CdbDebugEngine::continueInferior()
ULONG executionStatus;
HRESULT hr = m_d->m_pDebugControl->GetExecutionStatus(&executionStatus);
if (SUCCEEDED(hr) && executionStatus != DEBUG_STATUS_GO)
m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_GO);
startWatchTimer();
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
if (SUCCEEDED(hr) && executionStatus != DEBUG_STATUS_GO) {
hr = m_d->m_pDebugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if (SUCCEEDED(hr)) {
startWatchTimer();
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
} else {
qWarning("%s failed: %s", Q_FUNC_INFO, qPrintable(msgDebugEngineComResult(hr)));
}
}
}
void CdbDebugEngine::interruptInferior()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
qDebug() << Q_FUNC_INFO << m_d->m_hDebuggeeProcess;
//TODO: better use IDebugControl::SetInterrupt?
if (!m_d->m_hDebuggeeProcess)
return;
if (!DebugBreakProcess(m_d->m_hDebuggeeProcess)) {
qWarning("DebugBreakProcess failed.");
return;
if (DebugBreakProcess(m_d->m_hDebuggeeProcess)) {
m_d->m_debuggerManagerAccess->notifyInferiorStopped();
} else {
qWarning("DebugBreakProcess failed: %s", Core::Utils::winErrorMessage(GetLastError()));
}
m_d->m_debuggerManagerAccess->notifyInferiorStopped();
}
void CdbDebugEngine::runToLineExec(const QString &fileName, int lineNumber)
......@@ -571,43 +589,30 @@ void CdbDebugEngine::timerEvent(QTimerEvent* te)
if (te->timerId() != m_d->m_watchTimer)
return;
if (debugCDB > 1)
qDebug() << Q_FUNC_INFO;
const HRESULT hr = m_d->m_pDebugControl->WaitForEvent(0, 1);
if (debugCDB)
if (debugCDB > 1 || hr != S_FALSE)
qDebug() << Q_FUNC_INFO << "WaitForEvent" << msgDebugEngineComResult(hr);
HRESULT hr;
hr = m_d->m_pDebugControl->WaitForEvent(0, 1);
switch (hr) {
case S_OK:
if (debugCDB > 1)
qDebug() << "WaitForEvent S_OK";
killWatchTimer();
m_d->handleDebugEvent();
break;
case S_FALSE:
if (debugCDB > 1)
qDebug() << "WaitForEvent S_FALSE";
break;
case E_PENDING:
if (debugCDB > 1)
qDebug() << "WaitForEvent E_PENDING";
case E_FAIL:
break;
case E_UNEXPECTED:
if (debugCDB > 1)
qDebug() << "WaitForEvent E_UNEXPECTED";
killWatchTimer();
break;
case E_FAIL:
if (debugCDB > 1)
qDebug() << "WaitForEvent E_FAIL";
break;
break;
}
}
void CdbDebugEnginePrivate::handleDebugEvent()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
if (m_bIgnoreNextDebugEvent) {
m_engine->startWatchTimer();
......@@ -630,10 +635,18 @@ void CdbDebugEnginePrivate::handleDebugEvent()
//}
}
void CdbDebugEnginePrivate::setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << hDebuggeeProcess << hDebuggeeThread;
m_hDebuggeeProcess = hDebuggeeProcess;
m_hDebuggeeThread = hDebuggeeThread;
}
void CdbDebugEnginePrivate::updateThreadList()
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
ThreadsHandler* th = m_debuggerManagerAccess->threadsHandler();
QList<ThreadData> threads;
......
......@@ -62,6 +62,8 @@ struct CdbDebugEnginePrivate
explicit CdbDebugEnginePrivate(const DebuggerEngineLibrary &lib, DebuggerManager *parent, CdbDebugEngine* engine);
~CdbDebugEnginePrivate();
void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread);
bool isDebuggeeRunning() const { return m_watchTimer != -1; }
void handleDebugEvent();
void updateThreadList();
......
......@@ -91,6 +91,13 @@ STDMETHODIMP CdbDebugEventCallback::Breakpoint(THIS_ __in PDEBUG_BREAKPOINT Bp)
return S_OK;
}
static inline QString msgException(const EXCEPTION_RECORD64 *Exception, ULONG FirstChance)
{
return QString::fromLatin1("An exception occurred: Code=0x%1 FirstChance=%2").
arg(QString::number(Exception->ExceptionCode, 16)).
arg(FirstChance);
}
STDMETHODIMP CdbDebugEventCallback::Exception(
THIS_
__in PEXCEPTION_RECORD64 Exception,
......@@ -99,7 +106,11 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
{
Q_UNUSED(Exception)
if (debugCDB)
qDebug() << Q_FUNC_INFO << FirstChance;
qDebug() << Q_FUNC_INFO << msgException(Exception, FirstChance);
// First chance are harmless
if (!FirstChance)
qWarning("%s", qPrintable(msgException(Exception, FirstChance)));
return S_OK;
}
......@@ -161,8 +172,7 @@ STDMETHODIMP CdbDebugEventCallback::CreateProcess(
if (debugCDB)
qDebug() << Q_FUNC_INFO << ModuleName;
m_pEngine->m_d->m_hDebuggeeProcess = (HANDLE)Handle;
m_pEngine->m_d->m_hDebuggeeThread = (HANDLE)InitialThreadHandle;
m_pEngine->m_d->setDebuggeeHandles(reinterpret_cast<HANDLE>(Handle), reinterpret_cast<HANDLE>(InitialThreadHandle));
m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorRunning();
ULONG currentThreadId;
......@@ -183,8 +193,7 @@ STDMETHODIMP CdbDebugEventCallback::ExitProcess(
if (debugCDB)
qDebug() << Q_FUNC_INFO << ExitCode;
m_pEngine->m_d->m_hDebuggeeProcess = 0;
m_pEngine->m_d->m_hDebuggeeThread = 0;
m_pEngine->m_d->setDebuggeeHandles(0, 0);
m_pEngine->m_d->m_debuggerManagerAccess->notifyInferiorExited();
return S_OK;
}
......
......@@ -49,7 +49,72 @@
#include <QtGui/QProxyModel>
#include <QtGui/QSortFilterProxyModel>
using namespace Debugger::Internal;
namespace Debugger {
namespace Internal {
bool operator<(const ProcData &p1, const ProcData &p2)
{
return p1.name < p2.name;
}
// A filterable process list model
class ProcessListFilterModel : public QSortFilterProxyModel {
public:
explicit ProcessListFilterModel(QObject *parent);
QString processIdAt(const QModelIndex &index) const;
void populate(QList<ProcData> processes, const QString &excludePid = QString());
private:
QStandardItemModel *m_model;
};
ProcessListFilterModel::ProcessListFilterModel(QObject *parent) :
QSortFilterProxyModel(parent),
m_model(new QStandardItemModel(this))
{
QStringList columns;
columns << AttachExternalDialog::tr("Process ID")
<< AttachExternalDialog::tr("Name")
<< AttachExternalDialog::tr("State");
m_model->setHorizontalHeaderLabels(columns);
setSourceModel(m_model);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setFilterKeyColumn(1);
}
QString ProcessListFilterModel::processIdAt(const QModelIndex &index) const
{
if (index.isValid()) {
const QModelIndex index0 = mapToSource(index);
QModelIndex index = index0.sibling(index0.row(), 0);
if (const QStandardItem *item = m_model->itemFromIndex(index))
return item->text();
}
return QString();
}
void ProcessListFilterModel::populate(QList<ProcData> processes, const QString &excludePid)
{
qStableSort(processes);
if (const int rowCount = m_model->rowCount())
m_model->removeRows(0, rowCount);
QStandardItem *root = m_model->invisibleRootItem();
foreach(const ProcData &proc, processes) {
QList<QStandardItem *> row;
row.append(new QStandardItem(proc.ppid));
row.append(new QStandardItem(proc.name));
if (!proc.image.isEmpty())
row.back()->setToolTip(proc.image);
row.append(new QStandardItem(proc.state));
if (proc.ppid == excludePid)
foreach(QStandardItem *i, row)
i->setEnabled(false);
root->appendRow(row);
}
}
///////////////////////////////////////////////////////////////////////
//
......@@ -105,17 +170,6 @@ void AttachCoreDialog::setCoreFile(const QString &fileName)
//
///////////////////////////////////////////////////////////////////////
static QStandardItemModel *createProcessModel(QObject *parent)
{
QStandardItemModel *rc = new QStandardItemModel(parent);
QStringList columns;
columns << AttachExternalDialog::tr("Process ID")
<< AttachExternalDialog::tr("Name")
<< AttachExternalDialog::tr("State");
rc->setHorizontalHeaderLabels(columns);
return rc;
}
static bool isUnixProcessId(const QString &procname)
{
for (int i = 0; i != procname.size(); ++i)
......@@ -124,13 +178,6 @@ static bool isUnixProcessId(const QString &procname)
return true;
}
QT_BEGIN_NAMESPACE
bool operator<(const ProcData &p1, const ProcData &p2)
{
return p1.name < p2.name;
}
QT_END_NAMESPACE
// Determine UNIX processes by reading "/proc"
static QList<ProcData> unixProcessList()
{
......@@ -162,40 +209,13 @@ static QList<ProcData> unixProcessList()
return rc;
}
static inline QStandardItem *createStandardItem(const QString &text,
bool enabled)
{
QStandardItem *rc = new QStandardItem(text);
rc->setEnabled(enabled);
return rc;
}
// Populate a standard item model with a list
// of processes and gray out the excludePid.
static void populateProcessModel(QStandardItemModel *model,
const QString &excludePid = QString())
static QList<ProcData> processList()
{
#ifdef Q_OS_WIN
QList<ProcData> processes = winProcessList();
return winProcessList();
#else
QList<ProcData> processes = unixProcessList();
return unixProcessList();
#endif
qStableSort(processes);
if (const int rowCount = model->rowCount())
model->removeRows(0, rowCount);
QStandardItem *root = model->invisibleRootItem();
foreach(const ProcData &proc, processes) {
const bool enabled = proc.ppid != excludePid;
QList<QStandardItem *> row;
row.append(createStandardItem(proc.ppid, enabled));
row.append(createStandardItem(proc.name, enabled));
if (!proc.image.isEmpty())
row.back()->setToolTip(proc.image);
row.append(createStandardItem(proc.state, enabled));
root->appendRow(row);
}
}
///////////////////////////////////////////////////////////////////////
......@@ -208,17 +228,13 @@ AttachExternalDialog::AttachExternalDialog(QWidget *parent) :
QDialog(parent),
m_selfPid(QString::number(QCoreApplication::applicationPid())),
m_ui(new Ui::AttachExternalDialog),
m_proxyModel(new QSortFilterProxyModel(this)),
m_model(createProcessModel(this))
m_model(new ProcessListFilterModel(this))
{
m_ui->setupUi(this);
okButton()->setDefault(true);
okButton()->setEnabled(false);
m_proxyModel->setSourceModel(m_model);
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setFilterKeyColumn(1);
m_ui->procView->setModel(m_proxyModel);
m_ui->procView->setModel(m_model);
m_ui->procView->setSortingEnabled(true);
connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
......@@ -227,7 +243,8 @@ AttachExternalDialog::AttachExternalDialog(QWidget *parent) :
connect(refreshButton, SIGNAL(clicked()), this, SLOT(rebuildProcessList()));
m_ui->buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole);
connect(m_ui->procView, SIGNAL(activated(QModelIndex)),
// Do not use activated, will be single click in Oxygen
connect(m_ui->procView, SIGNAL(doubleClicked(QModelIndex)),
this, SLOT(procSelected(QModelIndex)));
connect(m_ui->pidLineEdit, SIGNAL(textChanged(QString)),
this, SLOT(pidChanged(QString)));
......@@ -235,7 +252,7 @@ AttachExternalDialog::AttachExternalDialog(QWidget *parent) :
connect(m_ui->filterClearToolButton, SIGNAL(clicked()),
m_ui->filterLineEdit, SLOT(clear()));
connect(m_ui->filterLineEdit, SIGNAL(textChanged(QString)),
m_proxyModel, SLOT(setFilterFixedString(QString)));
m_model, SLOT(setFilterFixedString(QString)));
rebuildProcessList();
}
......@@ -252,7 +269,7 @@ QPushButton *AttachExternalDialog::okButton() const
void AttachExternalDialog::rebuildProcessList()
{
populateProcessModel(m_model, m_selfPid);
m_model->populate(processList(), m_selfPid);
m_ui->procView->expandAll();
m_ui->procView->resizeColumnToContents(0);
m_ui->procView->resizeColumnToContents(1);
......@@ -260,10 +277,9 @@ void AttachExternalDialog::rebuildProcessList()
void AttachExternalDialog::procSelected(const QModelIndex &proxyIndex)
{
const QModelIndex index0 = m_proxyModel->mapToSource(proxyIndex);
QModelIndex index = index0.sibling(index0.row(), 0);
if (const QStandardItem *item = m_model->itemFromIndex(index)) {
m_ui->pidLineEdit->setText(item->text());
const QString proccessId = m_model->processIdAt(proxyIndex);
if (!proccessId.isEmpty()) {
m_ui->pidLineEdit->setText(proccessId);
if (okButton()->isEnabled())
okButton()->animateClick();
}
......@@ -276,7 +292,7 @@ int AttachExternalDialog::attachPID() const
void AttachExternalDialog::pidChanged(const QString &pid)
{
okButton()->setEnabled(!pid.isEmpty() && pid != m_selfPid);
okButton()->setEnabled(!pid.isEmpty() && pid != QLatin1String("0") && pid != m_selfPid);
}
///////////////////////////////////////////////////////////////////////
......@@ -288,7 +304,7 @@ void AttachExternalDialog::pidChanged(const QString &pid)
AttachRemoteDialog::AttachRemoteDialog(QWidget *parent, const QString &pid) :
QDialog(parent),
m_ui(new Ui::AttachRemoteDialog),
m_model(createProcessModel(this))
m_model(new ProcessListFilterModel(this))
{
m_ui->setupUi(this);
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
......@@ -300,9 +316,22 @@ AttachRemoteDialog::AttachRemoteDialog(QWidget *parent, const QString &pid) :
connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(m_ui->procView, SIGNAL(activated(QModelIndex)),
// Do not use activated, will be single click in Oxygen
connect(m_ui->procView, SIGNAL(doubleClicked(QModelIndex)),
this, SLOT(procSelected(QModelIndex)));
QPushButton *refreshButton = new QPushButton(tr("Refresh"));
connect(refreshButton, SIGNAL(clicked()), this, SLOT(rebuildProcessList()));
m_ui->buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole);
connect(m_ui->pidLineEdit, SIGNAL(textChanged(QString)),
this, SLOT(pidChanged(QString)));
connect(m_ui->filterClearToolButton, SIGNAL(clicked()),
m_ui->filterLineEdit, SLOT(clear()));
connect(m_ui->filterLineEdit, SIGNAL(textChanged(QString)),
m_model, SLOT(setFilterFixedString(QString)));