diff --git a/src/libs/utils/processinfo.cpp b/src/libs/utils/processinfo.cpp index 8139eba32f0..e9da56a66be 100644 --- a/src/libs/utils/processinfo.cpp +++ b/src/libs/utils/processinfo.cpp @@ -5,6 +5,7 @@ #include "algorithm.h" #include "qtcprocess.h" +#include "utilstr.h" #include #include @@ -31,11 +32,16 @@ bool ProcessInfo::operator<(const ProcessInfo &other) const return commandLine < other.commandLine; } -// Determine UNIX processes by reading "/proc". Default to ps if -// it does not exist - -static QList getLocalProcessesUsingProc(const FilePath &procDir) +static expected_str> getLocalProcessesUsingProc(const FilePath &devicePath) { + 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 \\; " "-exec test -f {}/cmdline \\; " "-exec echo -en 'p{}\\ne' \\; " @@ -45,8 +51,7 @@ static QList getLocalProcessesUsingProc(const FilePath &procDir) "-exec echo \\; " "-exec echo __SKIP_ME__ \\;"; - CommandLine cmd{procDir.withNewPath("find"), - {procDir.nativePath(), "-maxdepth", "1", "-type", "d", "-name", "[0-9]*"}}; + CommandLine cmd{find, {procDir.nativePath(), "-maxdepth", "1", "-type", "d", "-name", "[0-9]*"}}; cmd.addArgs(execs, CommandLine::Raw); @@ -54,6 +59,13 @@ static QList getLocalProcessesUsingProc(const FilePath &procDir) procProcess.setCommand(cmd); 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 processes; const auto lines = procProcess.readAllStandardOutput().split('\n'); @@ -95,12 +107,22 @@ static QList getLocalProcessesUsingProc(const FilePath &procDir) } // Determine UNIX processes by running ps -static QMap getLocalProcessDataUsingPs(const FilePath &deviceRoot, - const QString &column) +static expected_str> getLocalProcessDataUsingPs( + const FilePath &ps, const QString &column) { Process process; - process.setCommand({deviceRoot.withNewPath("ps"), {"-e", "-o", "pid," + column}}); + process.setCommand({ps, {"-e", "-o", "pid," + column}}); 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" const QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n')); @@ -114,21 +136,30 @@ static QMap getLocalProcessDataUsingPs(const FilePath &deviceRo return result; } -static QList getLocalProcessesUsingPs(const FilePath &deviceRoot) +static expected_str> getLocalProcessesUsingPs(const FilePath &deviceRoot) { QList 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, // exeNames only the file part of the executable's path. - const QMap exeNames = getLocalProcessDataUsingPs(deviceRoot, "comm"); - const QMap cmdLines = getLocalProcessDataUsingPs(deviceRoot, "args"); + const auto exeNames = getLocalProcessDataUsingPs(ps, "comm"); + 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(); if (pid <= 0) continue; - const QString cmdLine = cmdLines.value(pid); - if (cmdLines.isEmpty()) + const QString cmdLine = cmdLines->value(pid); + if (cmdLines->isEmpty()) continue; const QString exeName = it.value(); if (exeName.isEmpty()) @@ -142,11 +173,21 @@ static QList getLocalProcessesUsingPs(const FilePath &deviceRoot) return processes; } -static QList getProcessesUsingPidin(const FilePath &pidin) +static expected_str> 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.setCommand({pidin, {"-F", "%a %A {/%n}"}}); 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 processes; QStringList lines = process.readAllStandardOutput().split(QLatin1Char('\n')); @@ -176,25 +217,37 @@ static QList getProcessesUsingPidin(const FilePath &pidin) return Utils::sorted(std::move(processes)); } -static QList processInfoListUnix(const FilePath &deviceRoot) +static expected_str> 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); + return getLocalProcessesUsingPs(deviceRoot) + .or_else([&deviceRoot](const QString &error) { + return getProcessesUsingPidin(deviceRoot) + .transform_error([error](const QString &pidinError) { + return Tr::tr("Failed to run pidin: %1\n%2").arg(pidinError).arg(error); + }); + }) + .or_else([&deviceRoot](const QString &error) { + return getLocalProcessesUsingProc(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) QList 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)