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:
Marcus Tillmanns
2023-03-10 16:29:41 +01:00
parent b6a593e4f9
commit 2d15be91bf
19 changed files with 188 additions and 433 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -30,8 +30,6 @@ QtcPlugin {
"qnxdevice.h",
"qnxdevicewizard.cpp",
"qnxdevicewizard.h",
"qnxdeviceprocesslist.cpp",
"qnxdeviceprocesslist.h",
"qnxdevicetester.cpp",
"qnxdevicetester.h",
"qnxconfigurationmanager.cpp",

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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