forked from qt-creator/qt-creator
ProjectExplorer: Consolidate ProcessList
* Combined local and ssh process list retrieval into LocalProcessList * Combined QnxProcessList into LocalProcessList * Renamed LocalProcessList to ProcessList Change-Id: I230c575375e306c638e4ca3034fa2d7ed243a44c Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -3,14 +3,13 @@
|
||||
|
||||
#include "processinfo.h"
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "qtcprocess.h"
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
#include <QDir>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
#elif defined(Q_OS_WIN)
|
||||
#include "winutils.h"
|
||||
#ifdef QTCREATOR_PCH_H
|
||||
@@ -32,82 +31,64 @@ bool ProcessInfo::operator<(const ProcessInfo &other) const
|
||||
return commandLine < other.commandLine;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
|
||||
static bool isUnixProcessId(const QString &procname)
|
||||
{
|
||||
for (int i = 0; i != procname.size(); ++i)
|
||||
if (!procname.at(i).isDigit())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine UNIX processes by reading "/proc". Default to ps if
|
||||
// it does not exist
|
||||
|
||||
static const char procDirC[] = "/proc/";
|
||||
|
||||
static QList<ProcessInfo> getLocalProcessesUsingProc()
|
||||
static QList<ProcessInfo> getLocalProcessesUsingProc(const FilePath &procDir)
|
||||
{
|
||||
static const QString execs = "-exec test -f {}/exe \\; "
|
||||
"-exec test -f {}/cmdline \\; "
|
||||
"-exec echo -en 'p{}\\ne' \\; "
|
||||
"-exec readlink {}/exe \\; "
|
||||
"-exec echo -n c \\; "
|
||||
"-exec head -n 1 {}/cmdline \\; "
|
||||
"-exec echo \\; "
|
||||
"-exec echo __SKIP_ME__ \\;";
|
||||
|
||||
CommandLine cmd{procDir.withNewPath("find"),
|
||||
{procDir.nativePath(), "-maxdepth", "1", "-type", "d", "-name", "[0-9]*"}};
|
||||
|
||||
cmd.addArgs(execs, CommandLine::Raw);
|
||||
|
||||
QtcProcess procProcess;
|
||||
procProcess.setCommand(cmd);
|
||||
procProcess.runBlocking();
|
||||
|
||||
QList<ProcessInfo> processes;
|
||||
const QString procDirPath = QLatin1String(procDirC);
|
||||
const QDir procDir = QDir(QLatin1String(procDirC));
|
||||
const QStringList procIds = procDir.entryList();
|
||||
for (const QString &procId : procIds) {
|
||||
if (!isUnixProcessId(procId))
|
||||
continue;
|
||||
ProcessInfo proc;
|
||||
proc.processId = procId.toInt();
|
||||
const QString root = procDirPath + procId;
|
||||
|
||||
const QFile exeFile(root + QLatin1String("/exe"));
|
||||
proc.executable = exeFile.symLinkTarget();
|
||||
const auto lines = procProcess.readAllStandardOutput().split('\n');
|
||||
for (auto it = lines.begin(); it != lines.end(); ++it) {
|
||||
if (it->startsWith('p')) {
|
||||
ProcessInfo proc;
|
||||
bool ok;
|
||||
proc.processId = FilePath::fromUserInput(it->mid(1).trimmed()).fileName().toInt(&ok);
|
||||
QTC_ASSERT(ok, continue);
|
||||
++it;
|
||||
|
||||
QFile cmdLineFile(root + QLatin1String("/cmdline"));
|
||||
if (cmdLineFile.open(QIODevice::ReadOnly)) { // process may have exited
|
||||
const QList<QByteArray> tokens = cmdLineFile.readAll().split('\0');
|
||||
if (!tokens.isEmpty()) {
|
||||
if (proc.executable.isEmpty())
|
||||
proc.executable = QString::fromLocal8Bit(tokens.front());
|
||||
for (const QByteArray &t : tokens) {
|
||||
if (!proc.commandLine.isEmpty())
|
||||
proc.commandLine.append(QLatin1Char(' '));
|
||||
proc.commandLine.append(QString::fromLocal8Bit(t));
|
||||
}
|
||||
}
|
||||
QTC_ASSERT(it->startsWith('e'), continue);
|
||||
proc.executable = it->mid(1).trimmed();
|
||||
++it;
|
||||
|
||||
QTC_ASSERT(it->startsWith('c'), continue);
|
||||
proc.commandLine = it->mid(1).trimmed().replace('\0', ' ');
|
||||
if (!proc.commandLine.contains("__SKIP_ME__"))
|
||||
processes.append(proc);
|
||||
}
|
||||
|
||||
if (proc.executable.isEmpty()) {
|
||||
QFile statFile(root + QLatin1String("/stat"));
|
||||
if (statFile.open(QIODevice::ReadOnly)) {
|
||||
const QStringList data = QString::fromLocal8Bit(statFile.readAll()).split(QLatin1Char(' '));
|
||||
if (data.size() < 2)
|
||||
continue;
|
||||
proc.executable = data.at(1);
|
||||
proc.commandLine = data.at(1); // PPID is element 3
|
||||
if (proc.executable.startsWith(QLatin1Char('(')) && proc.executable.endsWith(QLatin1Char(')'))) {
|
||||
proc.executable.truncate(proc.executable.size() - 1);
|
||||
proc.executable.remove(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!proc.executable.isEmpty())
|
||||
processes.push_back(proc);
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
|
||||
// Determine UNIX processes by running ps
|
||||
static QMap<qint64, QString> getLocalProcessDataUsingPs(const QString &column)
|
||||
static QMap<qint64, QString> getLocalProcessDataUsingPs(const FilePath &deviceRoot,
|
||||
const QString &column)
|
||||
{
|
||||
QtcProcess process;
|
||||
process.setCommand({"ps", {"-e", "-o", "pid," + column}});
|
||||
process.start();
|
||||
if (!process.waitForFinished())
|
||||
return {};
|
||||
process.setCommand({deviceRoot.withNewPath("ps"), {"-e", "-o", "pid," + column}});
|
||||
process.runBlocking();
|
||||
|
||||
// Split "457 /Users/foo.app arg1 arg2"
|
||||
const QStringList lines = process.stdOut().split(QLatin1Char('\n'));
|
||||
const QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
|
||||
QMap<qint64, QString> result;
|
||||
for (int i = 1; i < lines.size(); ++i) { // Skip header
|
||||
const QString line = lines.at(i).trimmed();
|
||||
@@ -118,14 +99,14 @@ static QMap<qint64, QString> getLocalProcessDataUsingPs(const QString &column)
|
||||
return result;
|
||||
}
|
||||
|
||||
static QList<ProcessInfo> getLocalProcessesUsingPs()
|
||||
static QList<ProcessInfo> getLocalProcessesUsingPs(const FilePath &deviceRoot)
|
||||
{
|
||||
QList<ProcessInfo> processes;
|
||||
|
||||
// cmdLines are full command lines, usually with absolute path,
|
||||
// exeNames only the file part of the executable's path.
|
||||
const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs("comm");
|
||||
const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs("args");
|
||||
const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs(deviceRoot, "comm");
|
||||
const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs(deviceRoot, "args");
|
||||
|
||||
for (auto it = exeNames.begin(), end = exeNames.end(); it != end; ++it) {
|
||||
const qint64 pid = it.key();
|
||||
@@ -146,16 +127,68 @@ static QList<ProcessInfo> getLocalProcessesUsingPs()
|
||||
return processes;
|
||||
}
|
||||
|
||||
QList<ProcessInfo> ProcessInfo::processInfoList()
|
||||
static QList<ProcessInfo> getProcessesUsingPidin(const FilePath &pidin)
|
||||
{
|
||||
const QDir procDir = QDir(QLatin1String(procDirC));
|
||||
return procDir.exists() ? getLocalProcessesUsingProc() : getLocalProcessesUsingPs();
|
||||
QtcProcess process;
|
||||
process.setCommand({pidin, {"-F", "%a %A {/%n}"}});
|
||||
process.runBlocking();
|
||||
|
||||
QList<ProcessInfo> processes;
|
||||
QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
|
||||
if (lines.isEmpty())
|
||||
return processes;
|
||||
|
||||
lines.pop_front(); // drop headers
|
||||
const QRegularExpression re("\\s*(\\d+)\\s+(.*){(.*)}");
|
||||
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
const QRegularExpressionMatch match = re.match(line);
|
||||
if (match.hasMatch()) {
|
||||
const QStringList captures = match.capturedTexts();
|
||||
if (captures.size() == 4) {
|
||||
const int pid = captures[1].toInt();
|
||||
const QString args = captures[2];
|
||||
const QString exe = captures[3];
|
||||
ProcessInfo deviceProcess;
|
||||
deviceProcess.processId = pid;
|
||||
deviceProcess.executable = exe.trimmed();
|
||||
deviceProcess.commandLine = args.trimmed();
|
||||
processes.append(deviceProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Utils::sorted(std::move(processes));
|
||||
}
|
||||
|
||||
static QList<ProcessInfo> processInfoListUnix(const FilePath &deviceRoot)
|
||||
{
|
||||
const FilePath procDir = deviceRoot.withNewPath("/proc");
|
||||
const FilePath pidin = deviceRoot.withNewPath("pidin").searchInPath();
|
||||
|
||||
if (pidin.isExecutableFile())
|
||||
return getProcessesUsingPidin(pidin);
|
||||
|
||||
if (procDir.isReadableDir())
|
||||
return getLocalProcessesUsingProc(procDir);
|
||||
|
||||
return getLocalProcessesUsingPs(deviceRoot);
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
|
||||
QList<ProcessInfo> ProcessInfo::processInfoList(const FilePath &deviceRoot)
|
||||
{
|
||||
return processInfoListUnix(deviceRoot);
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_WIN)
|
||||
|
||||
QList<ProcessInfo> ProcessInfo::processInfoList()
|
||||
QList<ProcessInfo> ProcessInfo::processInfoList(const FilePath &deviceRoot)
|
||||
{
|
||||
if (deviceRoot.needsDevice())
|
||||
return processInfoListUnix(deviceRoot);
|
||||
|
||||
QList<ProcessInfo> processes;
|
||||
|
||||
PROCESSENTRY32 pe;
|
||||
|
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include "filepath.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
@@ -19,7 +21,7 @@ public:
|
||||
|
||||
bool operator<(const ProcessInfo &other) const;
|
||||
|
||||
static QList<ProcessInfo> processInfoList();
|
||||
static QList<ProcessInfo> processInfoList(const Utils::FilePath &deviceRoot = Utils::FilePath());
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||
#include <projectexplorer/devicesupport/idevicewidget.h>
|
||||
#include <projectexplorer/devicesupport/processlist.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
@@ -486,7 +487,7 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
|
||||
dockerCmd.addArgs({"/bin/sh", "-c"});
|
||||
|
||||
CommandLine exec("exec");
|
||||
exec.addCommandLineAsArgs(cmd);
|
||||
exec.addCommandLineAsArgs(cmd, CommandLine::Raw);
|
||||
|
||||
CommandLine echo("echo");
|
||||
echo.addArgs("__qtc$$qtc__", CommandLine::Raw);
|
||||
@@ -494,7 +495,7 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
|
||||
|
||||
dockerCmd.addCommandLineAsSingleArg(echo);
|
||||
} else {
|
||||
dockerCmd.addCommandLineAsArgs(cmd);
|
||||
dockerCmd.addCommandLineAsArgs(cmd, CommandLine::Raw);
|
||||
}
|
||||
|
||||
return dockerCmd;
|
||||
@@ -792,9 +793,9 @@ PortsGatheringMethod DockerDevice::portsGatheringMethod() const
|
||||
&Port::parseFromSedOutput};
|
||||
};
|
||||
|
||||
DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
|
||||
DeviceProcessList *DockerDevice::createProcessListModel(QObject *parent) const
|
||||
{
|
||||
return nullptr;
|
||||
return new ProcessList(sharedFromThis(), parent);
|
||||
}
|
||||
|
||||
DeviceTester *DockerDevice::createDeviceTester() const
|
||||
|
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
bool canAutoDetectPorts() const override;
|
||||
ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
|
||||
bool canCreateProcessModel() const override { return false; }
|
||||
bool canCreateProcessModel() const override { return true; }
|
||||
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||
bool hasDeviceTester() const override { return false; }
|
||||
ProjectExplorer::DeviceTester *createDeviceTester() const override;
|
||||
|
@@ -66,8 +66,7 @@ add_qtc_plugin(ProjectExplorer
|
||||
devicesupport/idevicefactory.cpp devicesupport/idevicefactory.h
|
||||
devicesupport/idevicefwd.h
|
||||
devicesupport/idevicewidget.h
|
||||
devicesupport/localprocesslist.cpp devicesupport/localprocesslist.h
|
||||
devicesupport/sshdeviceprocesslist.cpp devicesupport/sshdeviceprocesslist.h
|
||||
devicesupport/processlist.cpp devicesupport/processlist.h
|
||||
devicesupport/sshparameters.cpp devicesupport/sshparameters.h
|
||||
devicesupport/sshsettings.cpp devicesupport/sshsettings.h
|
||||
devicesupport/sshsettingspage.cpp devicesupport/sshsettingspage.h
|
||||
|
@@ -3,11 +3,11 @@
|
||||
|
||||
#include "desktopdevice.h"
|
||||
|
||||
#include "desktopprocesssignaloperation.h"
|
||||
#include "deviceprocesslist.h"
|
||||
#include "localprocesslist.h"
|
||||
#include "../projectexplorerconstants.h"
|
||||
#include "../projectexplorertr.h"
|
||||
#include "desktopprocesssignaloperation.h"
|
||||
#include "deviceprocesslist.h"
|
||||
#include "processlist.h"
|
||||
|
||||
#include <coreplugin/fileutils.h>
|
||||
|
||||
@@ -137,7 +137,7 @@ bool DesktopDevice::canCreateProcessModel() const
|
||||
|
||||
DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const
|
||||
{
|
||||
return new Internal::LocalProcessList(sharedFromThis(), parent);
|
||||
return new ProcessList(sharedFromThis(), parent);
|
||||
}
|
||||
|
||||
DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
|
||||
|
@@ -1,59 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "localprocesslist.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/idevice.h>
|
||||
#include <utils/processinfo.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
#include <unistd.h>
|
||||
#elif defined(Q_OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace ProjectExplorer {
|
||||
namespace Internal {
|
||||
|
||||
LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent)
|
||||
: DeviceProcessList(device, parent)
|
||||
{
|
||||
#if defined(Q_OS_UNIX)
|
||||
setOwnPid(getpid());
|
||||
#elif defined(Q_OS_WIN)
|
||||
setOwnPid(GetCurrentProcessId());
|
||||
#endif
|
||||
}
|
||||
|
||||
void LocalProcessList::doKillProcess(const ProcessInfo &processInfo)
|
||||
{
|
||||
DeviceProcessSignalOperation::Ptr signalOperation = device()->signalOperation();
|
||||
connect(signalOperation.data(), &DeviceProcessSignalOperation::finished,
|
||||
this, &LocalProcessList::reportDelayedKillStatus);
|
||||
signalOperation->killProcess(processInfo.processId);
|
||||
}
|
||||
|
||||
void LocalProcessList::handleUpdate()
|
||||
{
|
||||
reportProcessListUpdated(ProcessInfo::processInfoList());
|
||||
}
|
||||
|
||||
void LocalProcessList::doUpdate()
|
||||
{
|
||||
QTimer::singleShot(0, this, &LocalProcessList::handleUpdate);
|
||||
}
|
||||
|
||||
void LocalProcessList::reportDelayedKillStatus(const QString &errorMessage)
|
||||
{
|
||||
if (errorMessage.isEmpty())
|
||||
reportProcessKilled();
|
||||
else
|
||||
reportError(errorMessage);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ProjectExplorer
|
61
src/plugins/projectexplorer/devicesupport/processlist.cpp
Normal file
61
src/plugins/projectexplorer/devicesupport/processlist.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "processlist.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/idevice.h>
|
||||
#include <utils/processinfo.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
#include <unistd.h>
|
||||
#elif defined(Q_OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace ProjectExplorer {
|
||||
|
||||
ProcessList::ProcessList(const IDevice::ConstPtr &device, QObject *parent)
|
||||
: DeviceProcessList(device, parent)
|
||||
{
|
||||
#if defined(Q_OS_UNIX)
|
||||
setOwnPid(getpid());
|
||||
#elif defined(Q_OS_WIN)
|
||||
setOwnPid(GetCurrentProcessId());
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProcessList::doKillProcess(const ProcessInfo &processInfo)
|
||||
{
|
||||
m_signalOperation = device()->signalOperation();
|
||||
connect(m_signalOperation.data(),
|
||||
&DeviceProcessSignalOperation::finished,
|
||||
this,
|
||||
&ProcessList::reportDelayedKillStatus);
|
||||
m_signalOperation->killProcess(processInfo.processId);
|
||||
}
|
||||
|
||||
void ProcessList::handleUpdate()
|
||||
{
|
||||
reportProcessListUpdated(ProcessInfo::processInfoList(DeviceProcessList::device()->rootPath()));
|
||||
}
|
||||
|
||||
void ProcessList::doUpdate()
|
||||
{
|
||||
QTimer::singleShot(0, this, &ProcessList::handleUpdate);
|
||||
}
|
||||
|
||||
void ProcessList::reportDelayedKillStatus(const QString &errorMessage)
|
||||
{
|
||||
if (errorMessage.isEmpty())
|
||||
reportProcessKilled();
|
||||
else
|
||||
reportError(errorMessage);
|
||||
|
||||
m_signalOperation.reset();
|
||||
}
|
||||
|
||||
} // namespace ProjectExplorer
|
@@ -4,16 +4,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "deviceprocesslist.h"
|
||||
#include "idevice.h"
|
||||
|
||||
namespace ProjectExplorer {
|
||||
namespace Internal {
|
||||
|
||||
class LocalProcessList : public DeviceProcessList
|
||||
class PROJECTEXPLORER_EXPORT ProcessList : public DeviceProcessList
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LocalProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
|
||||
explicit ProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
void doUpdate() override;
|
||||
@@ -22,7 +22,9 @@ private:
|
||||
private:
|
||||
void handleUpdate();
|
||||
void reportDelayedKillStatus(const QString &errorMessage);
|
||||
|
||||
private:
|
||||
DeviceProcessSignalOperation::Ptr m_signalOperation;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ProjectExplorer
|
@@ -1,83 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "sshdeviceprocesslist.h"
|
||||
|
||||
#include "idevice.h"
|
||||
#include "../projectexplorertr.h"
|
||||
|
||||
#include <utils/processinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace ProjectExplorer {
|
||||
|
||||
class SshDeviceProcessListPrivate
|
||||
{
|
||||
public:
|
||||
QtcProcess m_process;
|
||||
DeviceProcessSignalOperation::Ptr m_signalOperation;
|
||||
};
|
||||
|
||||
SshDeviceProcessList::SshDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) :
|
||||
DeviceProcessList(device, parent), d(std::make_unique<SshDeviceProcessListPrivate>())
|
||||
{
|
||||
connect(&d->m_process, &QtcProcess::done, this, &SshDeviceProcessList::handleProcessDone);
|
||||
}
|
||||
|
||||
SshDeviceProcessList::~SshDeviceProcessList() = default;
|
||||
|
||||
void SshDeviceProcessList::doUpdate()
|
||||
{
|
||||
d->m_process.close();
|
||||
d->m_process.setCommand({device()->filePath("/bin/sh"), {"-c", listProcessesCommandLine()}});
|
||||
d->m_process.start();
|
||||
}
|
||||
|
||||
void SshDeviceProcessList::doKillProcess(const ProcessInfo &process)
|
||||
{
|
||||
d->m_signalOperation = device()->signalOperation();
|
||||
QTC_ASSERT(d->m_signalOperation, return);
|
||||
connect(d->m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
|
||||
this, &SshDeviceProcessList::handleKillProcessFinished);
|
||||
d->m_signalOperation->killProcess(process.processId);
|
||||
}
|
||||
|
||||
void SshDeviceProcessList::handleProcessDone()
|
||||
{
|
||||
if (d->m_process.result() == ProcessResult::FinishedWithSuccess) {
|
||||
reportProcessListUpdated(buildProcessList(d->m_process.cleanedStdOut()));
|
||||
} else {
|
||||
const QString errorString = d->m_process.exitStatus() == QProcess::NormalExit
|
||||
? Tr::tr("Process listing command failed with exit code %1.").arg(d->m_process.exitCode())
|
||||
: d->m_process.errorString();
|
||||
const QString stdErr = d->m_process.cleanedStdErr();
|
||||
const QString outputString
|
||||
= stdErr.isEmpty() ? stdErr : Tr::tr("Remote stderr was: %1").arg(stdErr);
|
||||
reportError(Utils::joinStrings({errorString, outputString}, '\n'));
|
||||
}
|
||||
setFinished();
|
||||
}
|
||||
|
||||
void SshDeviceProcessList::handleKillProcessFinished(const QString &errorString)
|
||||
{
|
||||
if (errorString.isEmpty())
|
||||
reportProcessKilled();
|
||||
else
|
||||
reportError(Tr::tr("Error: Kill process failed: %1").arg(errorString));
|
||||
setFinished();
|
||||
}
|
||||
|
||||
void SshDeviceProcessList::setFinished()
|
||||
{
|
||||
d->m_process.close();
|
||||
if (d->m_signalOperation) {
|
||||
d->m_signalOperation->disconnect(this);
|
||||
d->m_signalOperation.clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ProjectExplorer
|
@@ -1,36 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "deviceprocesslist.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
|
||||
class SshDeviceProcessListPrivate;
|
||||
|
||||
class PROJECTEXPLORER_EXPORT SshDeviceProcessList : public DeviceProcessList
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SshDeviceProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr);
|
||||
~SshDeviceProcessList() override;
|
||||
|
||||
private:
|
||||
void handleProcessDone();
|
||||
void handleKillProcessFinished(const QString &errorString);
|
||||
|
||||
virtual QString listProcessesCommandLine() const = 0;
|
||||
virtual QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const = 0;
|
||||
|
||||
void doUpdate() override;
|
||||
void doKillProcess(const Utils::ProcessInfo &process) override;
|
||||
|
||||
void setFinished();
|
||||
|
||||
const std::unique_ptr<SshDeviceProcessListPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace ProjectExplorer
|
@@ -227,8 +227,7 @@ Project {
|
||||
"idevicefactory.cpp", "idevicefactory.h",
|
||||
"idevicefwd.h",
|
||||
"idevicewidget.h",
|
||||
"localprocesslist.cpp", "localprocesslist.h",
|
||||
"sshdeviceprocesslist.cpp", "sshdeviceprocesslist.h",
|
||||
"processlist.cpp", "processlist.h",
|
||||
"sshparameters.cpp", "sshparameters.h",
|
||||
"sshsettings.cpp", "sshsettings.h",
|
||||
"sshsettingspage.cpp", "sshsettingspage.h",
|
||||
|
@@ -10,7 +10,6 @@ add_qtc_plugin(Qnx
|
||||
qnxdebugsupport.cpp qnxdebugsupport.h
|
||||
qnxdeployqtlibrariesdialog.cpp qnxdeployqtlibrariesdialog.h
|
||||
qnxdevice.cpp qnxdevice.h
|
||||
qnxdeviceprocesslist.cpp qnxdeviceprocesslist.h
|
||||
qnxdevicetester.cpp qnxdevicetester.h
|
||||
qnxdevicewizard.cpp qnxdevicewizard.h
|
||||
qnxplugin.cpp
|
||||
|
@@ -30,8 +30,6 @@ QtcPlugin {
|
||||
"qnxdevice.h",
|
||||
"qnxdevicewizard.cpp",
|
||||
"qnxdevicewizard.h",
|
||||
"qnxdeviceprocesslist.cpp",
|
||||
"qnxdeviceprocesslist.h",
|
||||
"qnxdevicetester.cpp",
|
||||
"qnxdevicetester.h",
|
||||
"qnxconfigurationmanager.cpp",
|
||||
|
@@ -6,7 +6,6 @@
|
||||
#include "qnxconstants.h"
|
||||
#include "qnxdeployqtlibrariesdialog.h"
|
||||
#include "qnxdevicetester.h"
|
||||
#include "qnxdeviceprocesslist.h"
|
||||
#include "qnxdevicewizard.h"
|
||||
#include "qnxtr.h"
|
||||
|
||||
@@ -121,11 +120,6 @@ PortsGatheringMethod QnxDevice::portsGatheringMethod() const
|
||||
};
|
||||
}
|
||||
|
||||
DeviceProcessList *QnxDevice::createProcessListModel(QObject *parent) const
|
||||
{
|
||||
return new QnxDeviceProcessList(sharedFromThis(), parent);
|
||||
}
|
||||
|
||||
DeviceTester *QnxDevice::createDeviceTester() const
|
||||
{
|
||||
return new QnxDeviceTester;
|
||||
|
@@ -16,7 +16,6 @@ public:
|
||||
static Ptr create() { return Ptr(new QnxDevice); }
|
||||
|
||||
ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
|
||||
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||
|
||||
ProjectExplorer::DeviceTester *createDeviceTester() const override;
|
||||
|
@@ -1,59 +0,0 @@
|
||||
// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "qnxdeviceprocesslist.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/idevice.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/processinfo.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Qnx::Internal {
|
||||
|
||||
QnxDeviceProcessList::QnxDeviceProcessList(
|
||||
const ProjectExplorer::IDevice::ConstPtr &device, QObject *parent)
|
||||
: ProjectExplorer::SshDeviceProcessList(device, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString QnxDeviceProcessList::listProcessesCommandLine() const
|
||||
{
|
||||
return QLatin1String("pidin -F '%a %A {/%n}'");
|
||||
}
|
||||
|
||||
QList<ProcessInfo> QnxDeviceProcessList::buildProcessList(const QString &listProcessesReply) const
|
||||
{
|
||||
QList<ProcessInfo> processes;
|
||||
QStringList lines = listProcessesReply.split(QLatin1Char('\n'));
|
||||
if (lines.isEmpty())
|
||||
return processes;
|
||||
|
||||
lines.pop_front(); // drop headers
|
||||
const QRegularExpression re("\\s*(\\d+)\\s+(.*){(.*)}");
|
||||
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
const QRegularExpressionMatch match = re.match(line);
|
||||
if (match.hasMatch()) {
|
||||
const QStringList captures = match.capturedTexts();
|
||||
if (captures.size() == 4) {
|
||||
const int pid = captures[1].toInt();
|
||||
const QString args = captures[2];
|
||||
const QString exe = captures[3];
|
||||
ProcessInfo deviceProcess;
|
||||
deviceProcess.processId = pid;
|
||||
deviceProcess.executable = exe.trimmed();
|
||||
deviceProcess.commandLine = args.trimmed();
|
||||
processes.append(deviceProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Utils::sorted(std::move(processes));
|
||||
}
|
||||
|
||||
} // Qnx::Internal
|
@@ -1,21 +0,0 @@
|
||||
// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
|
||||
|
||||
namespace Qnx::Internal {
|
||||
|
||||
class QnxDeviceProcessList : public ProjectExplorer::SshDeviceProcessList
|
||||
{
|
||||
public:
|
||||
explicit QnxDeviceProcessList(
|
||||
const ProjectExplorer::IDeviceConstPtr &device, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
QString listProcessesCommandLine() const override;
|
||||
QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const override;
|
||||
};
|
||||
|
||||
} // Qnx::Internal
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <projectexplorer/devicesupport/filetransfer.h>
|
||||
#include <projectexplorer/devicesupport/filetransferinterface.h>
|
||||
#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
|
||||
#include <projectexplorer/devicesupport/processlist.h>
|
||||
#include <projectexplorer/devicesupport/sshparameters.h>
|
||||
#include <projectexplorer/devicesupport/sshsettings.h>
|
||||
|
||||
@@ -51,9 +51,6 @@ namespace RemoteLinux {
|
||||
|
||||
const QByteArray s_pidMarker = "__qtc";
|
||||
|
||||
const char Delimiter0[] = "x--";
|
||||
const char Delimiter1[] = "---";
|
||||
|
||||
static Q_LOGGING_CATEGORY(linuxDeviceLog, "qtc.remotelinux.device", QtWarningMsg);
|
||||
#define DEBUG(x) qCDebug(linuxDeviceLog) << x << '\n'
|
||||
|
||||
@@ -274,77 +271,6 @@ private:
|
||||
IDevice::ConstPtr m_device;
|
||||
};
|
||||
|
||||
static QString visualizeNull(QString s)
|
||||
{
|
||||
return s.replace(QLatin1Char('\0'), QLatin1String("<null>"));
|
||||
}
|
||||
|
||||
class LinuxDeviceProcessList : public SshDeviceProcessList
|
||||
{
|
||||
public:
|
||||
LinuxDeviceProcessList(const IDevice::ConstPtr &device, QObject *parent)
|
||||
: SshDeviceProcessList(device, parent)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QString listProcessesCommandLine() const override
|
||||
{
|
||||
return QString::fromLatin1(
|
||||
"for dir in `ls -d /proc/[0123456789]*`; do "
|
||||
"test -d $dir || continue;" // Decrease the likelihood of a race condition.
|
||||
"echo $dir;"
|
||||
"cat $dir/cmdline;echo;" // cmdline does not end in newline
|
||||
"cat $dir/stat;"
|
||||
"readlink $dir/exe;"
|
||||
"printf '%1''%2';"
|
||||
"done").arg(QLatin1String(Delimiter0)).arg(QLatin1String(Delimiter1));
|
||||
}
|
||||
|
||||
QList<ProcessInfo> buildProcessList(const QString &listProcessesReply) const override
|
||||
{
|
||||
QList<ProcessInfo> processes;
|
||||
const QStringList lines = listProcessesReply.split(QString::fromLatin1(Delimiter0)
|
||||
+ QString::fromLatin1(Delimiter1), Qt::SkipEmptyParts);
|
||||
for (const QString &line : lines) {
|
||||
const QStringList elements = line.split(QLatin1Char('\n'));
|
||||
if (elements.count() < 4) {
|
||||
qDebug("%s: Expected four list elements, got %d. Line was '%s'.", Q_FUNC_INFO,
|
||||
int(elements.count()), qPrintable(visualizeNull(line)));
|
||||
continue;
|
||||
}
|
||||
bool ok;
|
||||
const int pid = elements.first().mid(6).toInt(&ok);
|
||||
if (!ok) {
|
||||
qDebug("%s: Expected number in %s. Line was '%s'.", Q_FUNC_INFO,
|
||||
qPrintable(elements.first()), qPrintable(visualizeNull(line)));
|
||||
continue;
|
||||
}
|
||||
QString command = elements.at(1);
|
||||
command.replace(QLatin1Char('\0'), QLatin1Char(' '));
|
||||
if (command.isEmpty()) {
|
||||
const QString &statString = elements.at(2);
|
||||
const int openParenPos = statString.indexOf(QLatin1Char('('));
|
||||
const int closedParenPos = statString.indexOf(QLatin1Char(')'), openParenPos);
|
||||
if (openParenPos == -1 || closedParenPos == -1)
|
||||
continue;
|
||||
command = QLatin1Char('[')
|
||||
+ statString.mid(openParenPos + 1, closedParenPos - openParenPos - 1)
|
||||
+ QLatin1Char(']');
|
||||
}
|
||||
|
||||
ProcessInfo process;
|
||||
process.processId = pid;
|
||||
process.commandLine = command;
|
||||
process.executable = elements.at(3);
|
||||
processes.append(process);
|
||||
}
|
||||
|
||||
return Utils::sorted(std::move(processes));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// LinuxDevicePrivate
|
||||
|
||||
class ShellThreadHandler;
|
||||
@@ -1094,7 +1020,7 @@ PortsGatheringMethod LinuxDevice::portsGatheringMethod() const
|
||||
|
||||
DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const
|
||||
{
|
||||
return new LinuxDeviceProcessList(sharedFromThis(), parent);
|
||||
return new ProcessList(sharedFromThis(), parent);
|
||||
}
|
||||
|
||||
DeviceTester *LinuxDevice::createDeviceTester() const
|
||||
|
Reference in New Issue
Block a user