Commit 86b6c44e authored by Friedemann Kleint's avatar Friedemann Kleint Committed by unknown

Process Listing Windows

parent 1e7d6a01
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AttachExternalDialog</class> <class>AttachExternalDialog</class>
<widget class="QDialog" name="AttachExternalDialog" > <widget class="QDialog" name="AttachExternalDialog">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
...@@ -9,50 +10,76 @@ ...@@ -9,50 +10,76 @@
<height>866</height> <height>866</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string>Start Debugger</string> <string>Start Debugger</string>
</property> </property>
<layout class="QVBoxLayout" > <layout class="QVBoxLayout">
<property name="spacing" > <property name="spacing">
<number>6</number> <number>6</number>
</property> </property>
<property name="margin" > <property name="margin">
<number>9</number> <number>9</number>
</property> </property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" > <layout class="QFormLayout" name="formLayout">
<item> <item row="0" column="0">
<widget class="QLabel" name="pidLabel" > <widget class="QLabel" name="pidLabel">
<property name="text" > <property name="text">
<string>Attach to Process ID:</string> <string>Attach to Process ID:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="0" column="1">
<widget class="QLineEdit" name="pidLineEdit" /> <widget class="QLineEdit" name="pidLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="filterLabel">
<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>Clear</string>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QTreeView" name="procView" > <widget class="QTreeView" name="procView">
<property name="editTriggers" > <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="Line" name="line" > <widget class="Line" name="line">
<property name="orientation" > <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox" > <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation" > <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons" > <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "stackhandler.h" #include "stackhandler.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/winutils.h>
#include <utils/consoleprocess.h> #include <utils/consoleprocess.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
...@@ -49,6 +50,31 @@ ...@@ -49,6 +50,31 @@
static const char *dbgEngineDllC = "dbgeng"; static const char *dbgEngineDllC = "dbgeng";
static const char *debugCreateFuncC = "DebugCreate"; static const char *debugCreateFuncC = "DebugCreate";
static QString debugEngineComError(HRESULT hr)
{
if (!FAILED(hr))
return QLatin1String("S_OK");
switch (hr) {
case E_FAIL:
break;
case E_INVALIDARG:
return QLatin1String("E_INVALIDARG");
case E_NOINTERFACE:
return QLatin1String("E_NOINTERFACE");
case E_OUTOFMEMORY:
return QLatin1String("E_OUTOFMEMORY");
case E_UNEXPECTED:
return QLatin1String("E_UNEXPECTED");
case E_NOTIMPL:
return QLatin1String("E_NOTIMPL");
}
if (hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED))
return QLatin1String("ERROR_ACCESS_DENIED");;
if (hr == HRESULT_FROM_NT(STATUS_CONTROL_C_EXIT))
return QLatin1String("STATUS_CONTROL_C_EXIT");
return Core::Utils::winErrorMessage(HRESULT_CODE(hr));
}
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
...@@ -177,13 +203,47 @@ void CdbDebugEngine::setToolTipExpression(const QPoint & /*pos*/, const QString ...@@ -177,13 +203,47 @@ void CdbDebugEngine::setToolTipExpression(const QPoint & /*pos*/, const QString
} }
bool CdbDebugEngine::startDebugger() bool CdbDebugEngine::startDebugger()
{ {
m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1); m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
QString errorMessage;
bool rc = false;
switch (m_d->m_debuggerManager->startMode()) {
case AttachExternal:
rc = startAttachDebugger(m_d->m_debuggerManager->m_attachedPID, &errorMessage);
break;
case StartInternal:
case StartExternal:
rc = startDebuggerWithExecutable(&errorMessage);
break;
case AttachCore:
errorMessage = tr("CdbDebugEngine: Attach to core not supported!");
break;
}
if (rc) {
m_d->m_debuggerManager->showStatusMessage(tr("Debugger Running"), -1);
startWatchTimer();
} else {
qWarning("%s\n", qPrintable(errorMessage));
}
return rc;
}
bool CdbDebugEngine::startAttachDebugger(unsigned long pid, QString *errorMessage)
{
const HRESULT hr = m_d->m_pDebugClient->AttachProcess(NULL, pid,
DEBUG_ATTACH_NONINVASIVE |DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND);
if (debugCDB)
qDebug() << "Attaching to " << pid << " returns " << hr;
if (FAILED(hr)) {
*errorMessage = tr("AttachProcess failed for pid %1: %2").arg(pid).arg(debugEngineComError(hr));
return false;
}
return true;
}
//if (!q->m_workingDir.isEmpty()) bool CdbDebugEngine::startDebuggerWithExecutable(QString *errorMessage)
// m_gdbProc.setWorkingDirectory(q->m_workingDir); {
//if (!q->m_environment.isEmpty()) m_d->m_debuggerManager->showStatusMessage("Starting Debugger", -1);
// m_gdbProc.setEnvironment(q->m_environment);
DEBUG_CREATE_PROCESS_OPTIONS dbgopts; DEBUG_CREATE_PROCESS_OPTIONS dbgopts;
memset(&dbgopts, 0, sizeof(dbgopts)); memset(&dbgopts, 0, sizeof(dbgopts));
...@@ -199,32 +259,28 @@ bool CdbDebugEngine::startDebugger() ...@@ -199,32 +259,28 @@ bool CdbDebugEngine::startDebugger()
m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS); m_d->m_pDebugSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS);
//m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH); //m_pDebugSymbols->AddSymbolOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST | SYMOPT_AUTO_PUBLICS | SYMOPT_NO_IMAGE_SEARCH);
if (m_d->m_debuggerManager->startMode() == AttachExternal) { // TODO console
qWarning("CdbDebugEngine: attach to process not yet implemented!"); const QString cmd = Core::Utils::AbstractProcess::createWinCommandline(filename, m_d->m_debuggerManager->m_processArgs);
if (debugCDB)
qDebug() << "Starting " << cmd;
PCWSTR env = 0;
QByteArray envData;
if (!m_d->m_debuggerManager->m_environment.empty()) {
envData = Core::Utils::AbstractProcess::createWinEnvironment(Core::Utils::AbstractProcess::fixWinEnvironment(m_d->m_debuggerManager->m_environment));
env = reinterpret_cast<PCWSTR>(envData.data());
}
const HRESULT hr = m_d->m_pDebugClient->CreateProcess2Wide(NULL,
const_cast<PWSTR>(cmd.utf16()),
&dbgopts,
sizeof(dbgopts),
m_d->m_debuggerManager->m_workingDir.utf16(),
env);
if (FAILED(hr)) {
*errorMessage = tr("CreateProcess2Wide failed for '%1': %2").arg(cmd).arg(debugEngineComError(hr));
m_d->m_debuggerManagerAccess->notifyInferiorExited();
return false; return false;
} else {
QString cmd = Core::Utils::AbstractProcess::createWinCommandline(filename, m_d->m_debuggerManager->m_processArgs);
PCWSTR env = 0;
QByteArray envData;
if (!m_d->m_debuggerManager->m_environment.empty()) {
envData = Core::Utils::AbstractProcess::createWinEnvironment(Core::Utils::AbstractProcess::fixWinEnvironment(m_d->m_debuggerManager->m_environment));
env = reinterpret_cast<PCWSTR>(envData.data());
}
HRESULT hr = m_d->m_pDebugClient->CreateProcess2Wide(NULL,
const_cast<PWSTR>(cmd.utf16()),
&dbgopts,
sizeof(dbgopts),
m_d->m_debuggerManager->m_workingDir.utf16(),
env);
if (FAILED(hr)) {
//qWarning("CreateProcess2Wide failed");
m_d->m_debuggerManagerAccess->notifyInferiorExited();
return false;
}
} }
m_d->m_debuggerManagerAccess->notifyInferiorRunning();
m_d->m_debuggerManager->showStatusMessage(tr("Debugger Running"), -1);
startWatchTimer();
return true; return true;
} }
......
...@@ -99,6 +99,8 @@ protected: ...@@ -99,6 +99,8 @@ protected:
void timerEvent(QTimerEvent*); void timerEvent(QTimerEvent*);
private: private:
bool startAttachDebugger(unsigned long pid, QString *errorMessage);
bool startDebuggerWithExecutable(QString *errorMessage);
void startWatchTimer(); void startWatchTimer();
void killWatchTimer(); void killWatchTimer();
......
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
#include "ui_attachremotedialog.h" #include "ui_attachremotedialog.h"
#include "ui_startexternaldialog.h" #include "ui_startexternaldialog.h"
#ifdef Q_OS_WIN
# include "dbgwinutils.h"
#endif
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QFile> #include <QtCore/QFile>
...@@ -41,17 +45,11 @@ ...@@ -41,17 +45,11 @@
#include <QtGui/QHeaderView> #include <QtGui/QHeaderView>
#include <QtGui/QFileDialog> #include <QtGui/QFileDialog>
#include <QtGui/QPushButton> #include <QtGui/QPushButton>
#include <QtGui/QProxyModel>
#ifdef Q_OS_WINDOWS #include <QtGui/QSortFilterProxyModel>
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#include <stdio.h>
#endif
using namespace Debugger::Internal; using namespace Debugger::Internal;
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// AttachCoreDialog // AttachCoreDialog
...@@ -100,34 +98,21 @@ void AttachCoreDialog::setCoreFile(const QString &fileName) ...@@ -100,34 +98,21 @@ void AttachCoreDialog::setCoreFile(const QString &fileName)
m_ui->coreFileName->setPath(fileName); m_ui->coreFileName->setPath(fileName);
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// AttachExternalDialog // process model helpers
// //
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
AttachExternalDialog::AttachExternalDialog(QWidget *parent) static QStandardItemModel *createProcessModel(QObject *parent)
: QDialog(parent), m_ui(new Ui::AttachExternalDialog)
{
m_ui->setupUi(this);
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
m_model = new QStandardItemModel(this);
m_ui->procView->setSortingEnabled(true);
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)),
this, SLOT(procSelected(QModelIndex)));
rebuildProcessList();
}
AttachExternalDialog::~AttachExternalDialog()
{ {
delete m_ui; 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 isProcessName(const QString &procname) static bool isProcessName(const QString &procname)
...@@ -138,87 +123,122 @@ static bool isProcessName(const QString &procname) ...@@ -138,87 +123,122 @@ static bool isProcessName(const QString &procname)
return true; return true;
} }
struct ProcData bool operator<(const ProcData &p1, const ProcData &p2)
{
QString ppid;
QString name;
QString state;
};
static void insertItem(QStandardItem *root, const QString &pid,
const QMap<QString, ProcData> &procs, QMap<QString, QStandardItem *> &known)
{ {
//qDebug() << "HANDLING " << pid; return p1.name < p2.name;
QStandardItem *parent = 0;
const ProcData &proc = procs[pid];
if (1 || pid == "0") { // FIXME: a real tree is not-so-nice to search
parent = root;
} else {
if (!known.contains(proc.ppid))
insertItem(root, proc.ppid, procs, known);
parent = known[proc.ppid];
}
QList<QStandardItem *> row;
row.append(new QStandardItem(pid));
row.append(new QStandardItem(proc.name));
//row.append(new QStandardItem(proc.ppid));
row.append(new QStandardItem(proc.state));
parent->appendRow(row);
known[pid] = row[0];
} }
void AttachExternalDialog::rebuildProcessList() // Determine UNIX processes by reading "/proc"
static QList<ProcData> unixProcessList()
{ {
QStringList procnames = QDir("/proc/").entryList(); QList<ProcData> rc;
if (procnames.isEmpty()) { const QStringList procnames = QDir(QLatin1String("/proc/")).entryList();
m_ui->procView->hide(); if (procnames.isEmpty())
return; return rc;
}
typedef QMap<QString, ProcData> Procs;
Procs procs;
foreach (const QString &procname, procnames) { foreach (const QString &procname, procnames) {
if (!isProcessName(procname)) if (!isProcessName(procname))
continue; continue;
QString filename = "/proc/" + procname + "/stat"; QString filename = QLatin1String("/proc/");
filename += procname;
filename += QLatin1String("/stat");
QFile file(filename); QFile file(filename);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
QStringList data = QString::fromLocal8Bit(file.readAll()).split(' '); const QStringList data = QString::fromLocal8Bit(file.readAll()).split(' ');
//qDebug() << filename << data;
ProcData proc; ProcData proc;
proc.name = data.at(1); proc.name = data.at(1);
if (proc.name.startsWith('(') && proc.name.endsWith(')')) if (proc.name.startsWith(QLatin1Char('(')) && proc.name.endsWith(QLatin1Char(')')))
proc.name = proc.name.mid(1, proc.name.size() - 2); proc.name = proc.name.mid(1, proc.name.size() - 2);
proc.state = data.at(2); proc.state = data.at(2);
proc.ppid = data.at(3); proc.ppid = data.at(3);
procs[procname] = proc; rc.push_back(proc);
} }
return rc;
}
m_model->clear(); static void populateProcessModel(QStandardItemModel *model)
QMap<QString, QStandardItem *> known; {
for (Procs::const_iterator it = procs.begin(); it != procs.end(); ++it) #ifdef Q_OS_WIN
insertItem(m_model->invisibleRootItem(), it.key(), procs, known); QList<ProcData> processes = winProcessList();
m_model->setHeaderData(0, Qt::Horizontal, "Process ID", Qt::DisplayRole); #else
m_model->setHeaderData(1, Qt::Horizontal, "Name", Qt::DisplayRole); QList<ProcData> processes = unixProcessList();
//model->setHeaderData(2, Qt::Horizontal, "Parent", Qt::DisplayRole); #endif
m_model->setHeaderData(2, Qt::Horizontal, "State", Qt::DisplayRole); qStableSort(processes);
if (const int rowCount = model->rowCount())
model->removeRows(0, rowCount);
QStandardItem *root = 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));
root->appendRow(row);
}
}
m_ui->procView->setModel(m_model); ///////////////////////////////////////////////////////////////////////
//
// AttachExternalDialog
//
///////////////////////////////////////////////////////////////////////
AttachExternalDialog::AttachExternalDialog(QWidget *parent) :
QDialog(parent),
m_ui(new Ui::AttachExternalDialog),
m_model(createProcessModel(this)),
m_proxyModel(new QSortFilterProxyModel(this))
{
m_ui->setupUi(this);
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
m_proxyModel->setSourceModel(m_model);
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setFilterKeyColumn(1);
m_ui->procView->setModel(m_proxyModel);
m_ui->procView->setSortingEnabled(true);
connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
QPushButton *refreshButton = new QPushButton(tr("Refresh"));
connect(refreshButton, SIGNAL(clicked()), this, SLOT(rebuildProcessList()));
m_ui->buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole);
connect(m_ui->procView, SIGNAL(activated(QModelIndex)),
this, SLOT(procSelected(QModelIndex)));
connect(m_ui->filterClearToolButton, SIGNAL(clicked()),
m_ui->filterLineEdit, SLOT(clear()));
connect(m_ui->filterLineEdit, SIGNAL(textChanged(QString)),
m_proxyModel, SLOT(setFilterFixedString(QString)));
rebuildProcessList();
}
AttachExternalDialog::~AttachExternalDialog()
{
delete m_ui;
}
void AttachExternalDialog::rebuildProcessList()
{
populateProcessModel(m_model);
m_ui->procView->expandAll(); m_ui->procView->expandAll();
m_ui->procView->resizeColumnToContents(0); m_ui->procView->resizeColumnToContents(0);
m_ui->procView->resizeColumnToContents(1); m_ui->procView->resizeColumnToContents(1);
m_ui->procView->sortByColumn(1, Qt::AscendingOrder); m_ui->procView->sortByColumn(1, Qt::AscendingOrder);
} }
void AttachExternalDialog::procSelected(const QModelIndex &index0) void AttachExternalDialog::procSelected(const QModelIndex &proxyIndex)
{ {
const QModelIndex index0 = m_proxyModel->mapToSource(proxyIndex);
QModelIndex index = index0.sibling(index0.row(), 0); QModelIndex index = index0.sibling(index0.row(), 0);
QStandardItem *item = m_model->itemFromIndex(index); if (const QStandardItem *item = m_model->itemFromIndex(index)) {
if (!item) m_ui->pidLineEdit->setText(item->text());
return; m_ui->buttonBox->button(QDialogButtonBox::Ok)->animateClick();
m_ui->pidLineEdit->setText(item->text()); }
accept();
}