forked from qt-creator/qt-creator
Utils: Improve speed of ProcessList
Using "find" is considerably slower than "ps" so we try
ps first before using find as a fallback.
We also add error output if all three methods fail.
Amends: 2d15be91bf
Fixes: QTCREATORBUG-32738
Change-Id: I19151c8d9f74e6fb1850319f60f41bfab64386e8
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "algorithm.h"
|
#include "algorithm.h"
|
||||||
#include "qtcprocess.h"
|
#include "qtcprocess.h"
|
||||||
|
#include "utilstr.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
@@ -31,11 +32,16 @@ bool ProcessInfo::operator<(const ProcessInfo &other) const
|
|||||||
return commandLine < other.commandLine;
|
return commandLine < other.commandLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine UNIX processes by reading "/proc". Default to ps if
|
static expected_str<QList<ProcessInfo>> getLocalProcessesUsingProc(const FilePath &devicePath)
|
||||||
// it does not exist
|
|
||||||
|
|
||||||
static QList<ProcessInfo> getLocalProcessesUsingProc(const FilePath &procDir)
|
|
||||||
{
|
{
|
||||||
|
const FilePath procDir = devicePath.withNewPath("/proc");
|
||||||
|
if (!procDir.exists())
|
||||||
|
return make_unexpected(Tr::tr("%1 does not exist").arg(procDir.toUserOutput()));
|
||||||
|
|
||||||
|
const FilePath find = devicePath.withNewPath("find").searchInPath();
|
||||||
|
if (!find.isExecutableFile())
|
||||||
|
return make_unexpected(Tr::tr("find is not an existing executable"));
|
||||||
|
|
||||||
static const QString execs = "-exec test -f {}/exe \\; "
|
static const QString execs = "-exec test -f {}/exe \\; "
|
||||||
"-exec test -f {}/cmdline \\; "
|
"-exec test -f {}/cmdline \\; "
|
||||||
"-exec echo -en 'p{}\\ne' \\; "
|
"-exec echo -en 'p{}\\ne' \\; "
|
||||||
@@ -45,8 +51,7 @@ static QList<ProcessInfo> getLocalProcessesUsingProc(const FilePath &procDir)
|
|||||||
"-exec echo \\; "
|
"-exec echo \\; "
|
||||||
"-exec echo __SKIP_ME__ \\;";
|
"-exec echo __SKIP_ME__ \\;";
|
||||||
|
|
||||||
CommandLine cmd{procDir.withNewPath("find"),
|
CommandLine cmd{find, {procDir.nativePath(), "-maxdepth", "1", "-type", "d", "-name", "[0-9]*"}};
|
||||||
{procDir.nativePath(), "-maxdepth", "1", "-type", "d", "-name", "[0-9]*"}};
|
|
||||||
|
|
||||||
cmd.addArgs(execs, CommandLine::Raw);
|
cmd.addArgs(execs, CommandLine::Raw);
|
||||||
|
|
||||||
@@ -54,6 +59,13 @@ static QList<ProcessInfo> getLocalProcessesUsingProc(const FilePath &procDir)
|
|||||||
procProcess.setCommand(cmd);
|
procProcess.setCommand(cmd);
|
||||||
procProcess.runBlocking();
|
procProcess.runBlocking();
|
||||||
|
|
||||||
|
// We can only check the errorString here. The exit code maybe != 0 if one of the "test"s failed.
|
||||||
|
if (!procProcess.errorString().isEmpty()) {
|
||||||
|
return make_unexpected(Tr::tr("Failed to run %1: %2")
|
||||||
|
.arg(cmd.executable().toUserOutput())
|
||||||
|
.arg(procProcess.errorString()));
|
||||||
|
}
|
||||||
|
|
||||||
QList<ProcessInfo> processes;
|
QList<ProcessInfo> processes;
|
||||||
|
|
||||||
const auto lines = procProcess.readAllStandardOutput().split('\n');
|
const auto lines = procProcess.readAllStandardOutput().split('\n');
|
||||||
@@ -95,12 +107,22 @@ static QList<ProcessInfo> getLocalProcessesUsingProc(const FilePath &procDir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine UNIX processes by running ps
|
// Determine UNIX processes by running ps
|
||||||
static QMap<qint64, QString> getLocalProcessDataUsingPs(const FilePath &deviceRoot,
|
static expected_str<QMap<qint64, QString>> getLocalProcessDataUsingPs(
|
||||||
const QString &column)
|
const FilePath &ps, const QString &column)
|
||||||
{
|
{
|
||||||
Process process;
|
Process process;
|
||||||
process.setCommand({deviceRoot.withNewPath("ps"), {"-e", "-o", "pid," + column}});
|
process.setCommand({ps, {"-e", "-o", "pid," + column}});
|
||||||
process.runBlocking();
|
process.runBlocking();
|
||||||
|
if (!process.errorString().isEmpty()) {
|
||||||
|
return make_unexpected(
|
||||||
|
Tr::tr("Failed to run %1: %2").arg(ps.toUserOutput()).arg(process.errorString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.exitCode() != 0) {
|
||||||
|
return make_unexpected(Tr::tr("Failed to run %1: %2")
|
||||||
|
.arg(ps.toUserOutput())
|
||||||
|
.arg(process.readAllStandardError()));
|
||||||
|
}
|
||||||
|
|
||||||
// Split "457 /Users/foo.app arg1 arg2"
|
// Split "457 /Users/foo.app arg1 arg2"
|
||||||
const QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
|
const QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
|
||||||
@@ -114,21 +136,30 @@ static QMap<qint64, QString> getLocalProcessDataUsingPs(const FilePath &deviceRo
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<ProcessInfo> getLocalProcessesUsingPs(const FilePath &deviceRoot)
|
static expected_str<QList<ProcessInfo>> getLocalProcessesUsingPs(const FilePath &deviceRoot)
|
||||||
{
|
{
|
||||||
QList<ProcessInfo> processes;
|
QList<ProcessInfo> processes;
|
||||||
|
|
||||||
|
const FilePath ps = deviceRoot.withNewPath("ps").searchInPath();
|
||||||
|
if (!ps.isExecutableFile())
|
||||||
|
return make_unexpected(Tr::tr("ps is not an existing executable"));
|
||||||
|
|
||||||
// cmdLines are full command lines, usually with absolute path,
|
// cmdLines are full command lines, usually with absolute path,
|
||||||
// exeNames only the file part of the executable's path.
|
// exeNames only the file part of the executable's path.
|
||||||
const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs(deviceRoot, "comm");
|
const auto exeNames = getLocalProcessDataUsingPs(ps, "comm");
|
||||||
const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs(deviceRoot, "args");
|
if (!exeNames)
|
||||||
|
return make_unexpected(exeNames.error());
|
||||||
|
|
||||||
for (auto it = exeNames.begin(), end = exeNames.end(); it != end; ++it) {
|
const auto cmdLines = getLocalProcessDataUsingPs(ps, "args");
|
||||||
|
if (!cmdLines)
|
||||||
|
return make_unexpected(cmdLines.error());
|
||||||
|
|
||||||
|
for (auto it = exeNames->begin(), end = exeNames->end(); it != end; ++it) {
|
||||||
const qint64 pid = it.key();
|
const qint64 pid = it.key();
|
||||||
if (pid <= 0)
|
if (pid <= 0)
|
||||||
continue;
|
continue;
|
||||||
const QString cmdLine = cmdLines.value(pid);
|
const QString cmdLine = cmdLines->value(pid);
|
||||||
if (cmdLines.isEmpty())
|
if (cmdLines->isEmpty())
|
||||||
continue;
|
continue;
|
||||||
const QString exeName = it.value();
|
const QString exeName = it.value();
|
||||||
if (exeName.isEmpty())
|
if (exeName.isEmpty())
|
||||||
@@ -142,11 +173,21 @@ static QList<ProcessInfo> getLocalProcessesUsingPs(const FilePath &deviceRoot)
|
|||||||
return processes;
|
return processes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<ProcessInfo> getProcessesUsingPidin(const FilePath &pidin)
|
static expected_str<QList<ProcessInfo>> getProcessesUsingPidin(const FilePath &deviceRoot)
|
||||||
{
|
{
|
||||||
|
const FilePath pidin = deviceRoot.withNewPath("pidin").searchInPath();
|
||||||
|
if (!pidin.isExecutableFile())
|
||||||
|
return make_unexpected(Tr::tr("pidin is not an existing executable"));
|
||||||
|
|
||||||
Process process;
|
Process process;
|
||||||
process.setCommand({pidin, {"-F", "%a %A {/%n}"}});
|
process.setCommand({pidin, {"-F", "%a %A {/%n}"}});
|
||||||
process.runBlocking();
|
process.runBlocking();
|
||||||
|
if (process.errorString().isEmpty()) {
|
||||||
|
return make_unexpected(
|
||||||
|
Tr::tr("Failed to run %1: %2").arg(pidin.toUserOutput()).arg(process.errorString()));
|
||||||
|
}
|
||||||
|
if (process.exitCode() != 0)
|
||||||
|
return make_unexpected(process.readAllStandardError());
|
||||||
|
|
||||||
QList<ProcessInfo> processes;
|
QList<ProcessInfo> processes;
|
||||||
QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
|
QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n'));
|
||||||
@@ -176,25 +217,37 @@ static QList<ProcessInfo> getProcessesUsingPidin(const FilePath &pidin)
|
|||||||
return Utils::sorted(std::move(processes));
|
return Utils::sorted(std::move(processes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<ProcessInfo> processInfoListUnix(const FilePath &deviceRoot)
|
static expected_str<QList<ProcessInfo>> processInfoListUnix(const FilePath &deviceRoot)
|
||||||
{
|
{
|
||||||
const FilePath procDir = deviceRoot.withNewPath("/proc");
|
return getLocalProcessesUsingPs(deviceRoot)
|
||||||
const FilePath pidin = deviceRoot.withNewPath("pidin").searchInPath();
|
.or_else([&deviceRoot](const QString &error) {
|
||||||
|
return getProcessesUsingPidin(deviceRoot)
|
||||||
if (pidin.isExecutableFile())
|
.transform_error([error](const QString &pidinError) {
|
||||||
return getProcessesUsingPidin(pidin);
|
return Tr::tr("Failed to run pidin: %1\n%2").arg(pidinError).arg(error);
|
||||||
|
});
|
||||||
if (procDir.isReadableDir())
|
})
|
||||||
return getLocalProcessesUsingProc(procDir);
|
.or_else([&deviceRoot](const QString &error) {
|
||||||
|
return getLocalProcessesUsingProc(deviceRoot)
|
||||||
return getLocalProcessesUsingPs(deviceRoot);
|
.transform_error([error](const QString &procError) {
|
||||||
|
return Tr::tr("Failed to run /proc: %1\n%2").arg(procError).arg(error);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.transform_error([](const QString &error) {
|
||||||
|
return Tr::tr("Failed to retrieve process information: %1").arg(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_UNIX)
|
||||||
|
|
||||||
QList<ProcessInfo> ProcessInfo::processInfoList(const FilePath &deviceRoot)
|
QList<ProcessInfo> ProcessInfo::processInfoList(const FilePath &deviceRoot)
|
||||||
{
|
{
|
||||||
return processInfoListUnix(deviceRoot);
|
auto result = processInfoListUnix(deviceRoot);
|
||||||
|
if (!result) {
|
||||||
|
qWarning().noquote() << result.error();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
|
Reference in New Issue
Block a user