File indexing completed on 2024-05-19 13:20:56
0001 /* 0002 SPDX-FileCopyrightText: 2007 John Tapsell <tapsell@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "processes.h" 0008 #include "processes_atop_p.h" 0009 #include "processes_base_p.h" 0010 #include "processes_local_p.h" 0011 #include "processes_remote_p.h" 0012 0013 #include <QByteArray> 0014 #include <QElapsedTimer> 0015 #include <QMutableSetIterator> 0016 #include <QSet> 0017 #include <QVariantMap> 0018 0019 // for sysconf 0020 #include <unistd.h> 0021 0022 /* if porting to an OS without signal.h please #define SIGTERM to something */ 0023 #include <signal.h> 0024 0025 namespace KSysGuard 0026 { 0027 class Q_DECL_HIDDEN Processes::Private 0028 { 0029 public: 0030 Private(Processes *q_ptr) 0031 { 0032 mFakeProcess.setParent(&mFakeProcess); 0033 mAbstractProcesses = nullptr; 0034 mHistoricProcesses = nullptr; 0035 mIsLocalHost = true; 0036 mProcesses.insert(-1, &mFakeProcess); 0037 mElapsedTimeMilliSeconds = 0; 0038 mUpdateFlags = {}; 0039 mUsingHistoricalData = false; 0040 mLastError = Error::NoError; 0041 q = q_ptr; 0042 } 0043 ~Private(); 0044 void markProcessesAsEnded(long pid); 0045 0046 QSet<long> mToBeProcessed; 0047 QSet<long> mEndedProcesses; ///< Processes that have finished 0048 0049 QHash<long, Process *> mProcesses; ///< This must include mFakeProcess at pid -1 0050 QList<Process *> mListProcesses; ///< A list of the processes. Does not include mFakeProcesses 0051 Process mFakeProcess; ///< A fake process with pid -1 just so that even init points to a parent 0052 0053 AbstractProcesses *mAbstractProcesses; ///< The OS specific code to get the process information 0054 ProcessesATop *mHistoricProcesses; ///< A way to get historic information about processes 0055 bool mIsLocalHost; ///< Whether this is localhost or not 0056 0057 QElapsedTimer mLastUpdated; ///< This is the time we last updated. Used to calculate cpu usage. 0058 long mElapsedTimeMilliSeconds; ///< The number of milliseconds (1000ths of a second) that passed since the last update 0059 0060 Processes::UpdateFlags mUpdateFlags; 0061 bool mUsingHistoricalData; ///< Whether to return historical data for updateProcess() etc 0062 Processes *q; 0063 0064 Error mLastError; 0065 }; 0066 0067 Processes::Private::~Private() 0068 { 0069 Q_FOREACH (Process *process, mProcesses) { 0070 if (process != &mFakeProcess) 0071 delete process; 0072 } 0073 mProcesses.clear(); 0074 mListProcesses.clear(); 0075 delete mAbstractProcesses; 0076 mAbstractProcesses = nullptr; 0077 delete mHistoricProcesses; 0078 mHistoricProcesses = nullptr; 0079 } 0080 0081 Processes::Processes(const QString &host, QObject *parent) 0082 : QObject(parent) 0083 , d(new Private(this)) 0084 { 0085 qRegisterMetaType<KSysGuard::Process::Updates>(); 0086 0087 if (host.isEmpty()) { 0088 d->mAbstractProcesses = new ProcessesLocal(); 0089 } else { 0090 ProcessesRemote *remote = new ProcessesRemote(host); 0091 d->mAbstractProcesses = remote; 0092 connect(remote, &ProcessesRemote::runCommand, this, &Processes::runCommand); 0093 } 0094 d->mIsLocalHost = host.isEmpty(); 0095 connect(d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated); 0096 connect(d->mAbstractProcesses, &AbstractProcesses::processUpdated, this, &Processes::processUpdated); 0097 } 0098 Processes::~Processes() 0099 { 0100 delete d; 0101 } 0102 0103 Processes::Error Processes::lastError() const 0104 { 0105 return d->mLastError; 0106 } 0107 Process *Processes::getProcess(long pid) const 0108 { 0109 return d->mProcesses.value(pid); 0110 } 0111 0112 const QList<Process *> &Processes::getAllProcesses() const 0113 { 0114 return d->mListProcesses; 0115 } 0116 0117 int Processes::processCount() const 0118 { 0119 return d->mListProcesses.count(); 0120 } 0121 0122 bool Processes::updateProcess(Process *ps, long ppid) 0123 { 0124 Process *parent = d->mProcesses.value(ppid, &d->mFakeProcess); 0125 Q_ASSERT(parent); // even init has a non-null parent - the mFakeProcess 0126 0127 if (ps->parent() != parent) { 0128 Q_EMIT beginMoveProcess(ps, parent /*new parent*/); 0129 // Processes has been reparented 0130 Process *p = ps; 0131 do { 0132 p = p->parent(); 0133 p->numChildren() -= (ps->numChildren() + 1); 0134 } while (p->pid() != -1); 0135 Q_ASSERT(ps != parent); 0136 ps->parent()->children().removeAll(ps); 0137 ps->setParent(parent); // the parent has changed 0138 parent->children().append(ps); 0139 p = ps; 0140 do { 0141 p = p->parent(); 0142 p->numChildren() += (ps->numChildren() + 1); 0143 } while (p->pid() != -1); 0144 Q_EMIT endMoveProcess(); 0145 Q_ASSERT(ps != parent); 0146 ps->setParent(parent); 0147 } 0148 0149 ps->setParentPid(ppid); 0150 0151 bool success = updateProcessInfo(ps); 0152 Q_EMIT processChanged(ps, false); 0153 0154 return success; 0155 } 0156 0157 bool Processes::updateProcessInfo(Process *ps) 0158 { 0159 // Now we can actually get the process info 0160 qlonglong oldUserTime = ps->userTime(); 0161 qlonglong oldSysTime = ps->sysTime(); 0162 0163 qlonglong oldIoCharactersRead = 0; 0164 qlonglong oldIoCharactersWritten = 0; 0165 qlonglong oldIoReadSyscalls = 0; 0166 qlonglong oldIoWriteSyscalls = 0; 0167 qlonglong oldIoCharactersActuallyRead = 0; 0168 qlonglong oldIoCharactersActuallyWritten = 0; 0169 0170 if (d->mUpdateFlags.testFlag(Processes::IOStatistics)) { 0171 oldIoCharactersRead = ps->ioCharactersRead(); 0172 oldIoCharactersWritten = ps->ioCharactersWritten(); 0173 oldIoReadSyscalls = ps->ioReadSyscalls(); 0174 oldIoWriteSyscalls = ps->ioWriteSyscalls(); 0175 oldIoCharactersActuallyRead = ps->ioCharactersActuallyRead(); 0176 oldIoCharactersActuallyWritten = ps->ioCharactersActuallyWritten(); 0177 } 0178 0179 ps->setChanges(Process::Nothing); 0180 bool success; 0181 if (d->mUsingHistoricalData) 0182 success = d->mHistoricProcesses->updateProcessInfo(ps->pid(), ps); 0183 else 0184 success = d->mAbstractProcesses->updateProcessInfo(ps->pid(), ps); 0185 0186 // Now we have the process info. Calculate the cpu usage and total cpu usage for itself and all its parents 0187 if (!d->mUsingHistoricalData && d->mElapsedTimeMilliSeconds != 0) { // Update the user usage and sys usage 0188 #ifndef Q_OS_NETBSD 0189 /* The elapsed time is the d->mElapsedTimeMilliSeconds 0190 * (which is of the order 2 seconds or so) plus a small 0191 * correction where we get the amount of time elapsed since 0192 * we start processing. This is because the processing itself 0193 * can take a non-trivial amount of time. */ 0194 int elapsedTime = ps->elapsedTimeMilliSeconds(); 0195 ps->setElapsedTimeMilliSeconds(d->mLastUpdated.elapsed()); 0196 elapsedTime = ps->elapsedTimeMilliSeconds() - elapsedTime + d->mElapsedTimeMilliSeconds; 0197 if (elapsedTime > 0) { 0198 ps->setUserUsage((int)(((ps->userTime() - oldUserTime) * 1000.0) / elapsedTime)); 0199 ps->setSysUsage((int)(((ps->sysTime() - oldSysTime) * 1000.0) / elapsedTime)); 0200 } 0201 #endif 0202 0203 static auto calculateRate = [](qlonglong current, qlonglong previous, int elapsedTime) { 0204 if (elapsedTime <= 0 || previous <= 0) { 0205 return 0.0; 0206 } 0207 return (current - previous) * 1000.0 / elapsedTime; 0208 }; 0209 0210 if (d->mUpdateFlags.testFlag(Processes::IOStatistics)) { 0211 ps->setIoCharactersReadRate(calculateRate(ps->ioCharactersRead(), oldIoCharactersRead, elapsedTime)); 0212 ps->setIoCharactersWrittenRate(calculateRate(ps->ioCharactersWritten(), oldIoCharactersWritten, elapsedTime)); 0213 ps->setIoReadSyscallsRate(calculateRate(ps->ioReadSyscalls(), oldIoReadSyscalls, elapsedTime)); 0214 ps->setIoWriteSyscallsRate(calculateRate(ps->ioWriteSyscalls(), oldIoWriteSyscalls, elapsedTime)); 0215 ps->setIoCharactersActuallyReadRate(calculateRate(ps->ioCharactersActuallyRead(), oldIoCharactersActuallyRead, elapsedTime)); 0216 ps->setIoCharactersActuallyWrittenRate(calculateRate(ps->ioCharactersActuallyWritten(), oldIoCharactersActuallyWritten, elapsedTime)); 0217 } else { 0218 ps->setIoCharactersReadRate(0); 0219 ps->setIoCharactersWrittenRate(0); 0220 ps->setIoReadSyscallsRate(0); 0221 ps->setIoWriteSyscallsRate(0); 0222 ps->setIoCharactersActuallyReadRate(0); 0223 ps->setIoCharactersActuallyWrittenRate(0); 0224 } 0225 } 0226 if (d->mUsingHistoricalData || d->mElapsedTimeMilliSeconds != 0) { 0227 ps->setTotalUserUsage(ps->userUsage()); 0228 ps->setTotalSysUsage(ps->sysUsage()); 0229 if (ps->userUsage() != 0 || ps->sysUsage() != 0) { 0230 Process *p = ps->parent(); 0231 while (p->pid() != -1) { 0232 p->totalUserUsage() += ps->userUsage(); 0233 p->totalSysUsage() += ps->sysUsage(); 0234 Q_EMIT processChanged(p, true); 0235 p = p->parent(); 0236 } 0237 } 0238 } 0239 0240 return success; 0241 } 0242 0243 bool Processes::addProcess(long pid, long ppid) 0244 { 0245 Process *parent = d->mProcesses.value(ppid); 0246 if (!parent) { 0247 // Under race conditions, the parent could have already quit 0248 // In this case, attach to top leaf 0249 parent = &d->mFakeProcess; 0250 Q_ASSERT(parent); // even init has a non-null parent - the mFakeProcess 0251 } 0252 // it's a new process - we need to set it up 0253 Process *ps = new Process(pid, ppid, parent); 0254 0255 Q_EMIT beginAddProcess(ps); 0256 0257 d->mProcesses.insert(pid, ps); 0258 0259 ps->setIndex(d->mListProcesses.count()); 0260 d->mListProcesses.append(ps); 0261 0262 ps->parent()->children().append(ps); 0263 Process *p = ps; 0264 do { 0265 Q_ASSERT(p); 0266 p = p->parent(); 0267 p->numChildren()++; 0268 } while (p->pid() != -1); 0269 ps->setParentPid(ppid); 0270 0271 // Now we can actually get the process info 0272 bool success = updateProcessInfo(ps); 0273 Q_EMIT endAddProcess(); 0274 return success; 0275 } 0276 bool Processes::updateOrAddProcess(long pid) 0277 { 0278 long ppid; 0279 if (d->mUsingHistoricalData) 0280 ppid = d->mHistoricProcesses->getParentPid(pid); 0281 else 0282 ppid = d->mAbstractProcesses->getParentPid(pid); 0283 0284 if (ppid == pid) // Shouldn't ever happen 0285 ppid = -1; 0286 0287 if (d->mToBeProcessed.contains(ppid)) { 0288 // Make sure that we update the parent before we update this one. Just makes things a bit easier. 0289 d->mToBeProcessed.remove(ppid); 0290 updateOrAddProcess(ppid); 0291 } 0292 0293 Process *ps = d->mProcesses.value(pid); 0294 if (!ps) 0295 return addProcess(pid, ppid); 0296 else 0297 return updateProcess(ps, ppid); 0298 } 0299 0300 void Processes::updateAllProcesses(long updateDurationMS, Processes::UpdateFlags updateFlags) 0301 { 0302 d->mUpdateFlags = updateFlags; 0303 0304 if (d->mUsingHistoricalData || d->mLastUpdated.elapsed() >= updateDurationMS || !d->mLastUpdated.isValid()) { 0305 d->mElapsedTimeMilliSeconds = d->mLastUpdated.restart(); 0306 if (d->mUsingHistoricalData) 0307 d->mHistoricProcesses->updateAllProcesses(d->mUpdateFlags); 0308 else 0309 d->mAbstractProcesses->updateAllProcesses(d->mUpdateFlags); // For a local machine, this will directly call Processes::processesUpdated() 0310 } 0311 } 0312 0313 void Processes::processesUpdated() 0314 { 0315 // First really delete any processes that ended last time 0316 long pid; 0317 { 0318 QSetIterator<long> i(d->mEndedProcesses); 0319 while (i.hasNext()) { 0320 pid = i.next(); 0321 deleteProcess(pid); 0322 } 0323 } 0324 0325 if (d->mUsingHistoricalData) 0326 d->mToBeProcessed = d->mHistoricProcesses->getAllPids(); 0327 else 0328 d->mToBeProcessed = d->mAbstractProcesses->getAllPids(); 0329 0330 QSet<long> endedProcesses; 0331 for (Process *p : d->mListProcesses) { 0332 if (!d->mToBeProcessed.contains(p->pid())) { 0333 endedProcesses += p->pid(); 0334 } 0335 } 0336 0337 { 0338 QMutableSetIterator<long> i(d->mToBeProcessed); 0339 while (i.hasNext()) { 0340 pid = i.next(); 0341 i.remove(); 0342 updateOrAddProcess(pid); // This adds the process or changes an existing one 0343 i.toFront(); // we can remove entries from this set elsewhere, so our iterator might be invalid. Reset it back to the start of the set 0344 } 0345 } 0346 { 0347 QSetIterator<long> i(endedProcesses); 0348 while (i.hasNext()) { 0349 // We saw these pids last time, but not this time. That means we have to mark them for deletion now 0350 pid = i.next(); 0351 d->markProcessesAsEnded(pid); 0352 } 0353 d->mEndedProcesses = endedProcesses; 0354 } 0355 0356 Q_EMIT updated(); 0357 } 0358 0359 void Processes::processUpdated(long pid, const Process::Updates &changes) 0360 { 0361 auto process = d->mProcesses.value(pid); 0362 if (!process) { 0363 return; 0364 } 0365 0366 for (auto entry : changes) { 0367 switch (entry.first) { 0368 case Process::VmPSS: 0369 process->setVmPSS(entry.second.toLongLong()); 0370 break; 0371 default: 0372 break; 0373 } 0374 } 0375 0376 Q_EMIT processChanged(process, false); 0377 } 0378 0379 void Processes::Private::markProcessesAsEnded(long pid) 0380 { 0381 Q_ASSERT(pid >= 0); 0382 0383 Process *process = mProcesses.value(pid); 0384 if (!process) 0385 return; 0386 process->setStatus(Process::Ended); 0387 Q_EMIT q->processChanged(process, false); 0388 } 0389 void Processes::deleteProcess(long pid) 0390 { 0391 Q_ASSERT(pid >= 0); 0392 0393 Process *process = d->mProcesses.value(pid); 0394 if (!process) 0395 return; 0396 Q_FOREACH (Process *it, process->children()) { 0397 deleteProcess(it->pid()); 0398 } 0399 0400 Q_EMIT beginRemoveProcess(process); 0401 0402 d->mProcesses.remove(pid); 0403 d->mListProcesses.removeAll(process); 0404 process->parent()->children().removeAll(process); 0405 Process *p = process; 0406 do { 0407 Q_ASSERT(p); 0408 p = p->parent(); 0409 p->numChildren()--; 0410 } while (p->pid() != -1); 0411 #ifndef QT_NO_DEBUG 0412 int i = 0; 0413 #endif 0414 Q_FOREACH (Process *it, d->mListProcesses) { 0415 if (it->index() > process->index()) 0416 it->setIndex(it->index() - 1); 0417 #ifndef QT_NO_DEBUG 0418 Q_ASSERT(it->index() == i++); 0419 #endif 0420 } 0421 0422 delete process; 0423 Q_EMIT endRemoveProcess(); 0424 } 0425 0426 bool Processes::killProcess(long pid) 0427 { 0428 return sendSignal(pid, SIGTERM); 0429 } 0430 0431 bool Processes::sendSignal(long pid, int sig) 0432 { 0433 auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses; 0434 auto error = processes->sendSignal(pid, sig); 0435 if (error != NoError) { 0436 d->mLastError = error; 0437 return false; 0438 } 0439 return true; 0440 } 0441 0442 bool Processes::setNiceness(long pid, int priority) 0443 { 0444 auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses; 0445 auto error = processes->setNiceness(pid, priority); 0446 if (error != NoError) { 0447 d->mLastError = error; 0448 return false; 0449 } 0450 return true; 0451 } 0452 0453 bool Processes::setScheduler(long pid, KSysGuard::Process::Scheduler priorityClass, int priority) 0454 { 0455 auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses; 0456 auto error = processes->setScheduler(pid, priorityClass, priority); 0457 if (error != NoError) { 0458 d->mLastError = error; 0459 return false; 0460 } 0461 return true; 0462 } 0463 0464 bool Processes::setIoNiceness(long pid, KSysGuard::Process::IoPriorityClass priorityClass, int priority) 0465 { 0466 auto processes = d->mUsingHistoricalData ? d->mHistoricProcesses : d->mAbstractProcesses; 0467 auto error = processes->setIoNiceness(pid, priorityClass, priority); 0468 if (error != NoError) { 0469 d->mLastError = error; 0470 return false; 0471 } 0472 return true; 0473 } 0474 0475 bool Processes::supportsIoNiceness() 0476 { 0477 if (d->mUsingHistoricalData) 0478 return d->mHistoricProcesses->supportsIoNiceness(); 0479 return d->mAbstractProcesses->supportsIoNiceness(); 0480 } 0481 0482 long long Processes::totalPhysicalMemory() 0483 { 0484 return d->mAbstractProcesses->totalPhysicalMemory(); 0485 } 0486 0487 long Processes::numberProcessorCores() 0488 { 0489 return d->mAbstractProcesses->numberProcessorCores(); 0490 } 0491 0492 void Processes::answerReceived(int id, const QList<QByteArray> &answer) 0493 { 0494 KSysGuard::ProcessesRemote *processes = qobject_cast<KSysGuard::ProcessesRemote *>(d->mAbstractProcesses); 0495 if (processes) 0496 processes->answerReceived(id, answer); 0497 } 0498 0499 QList<QPair<QDateTime, uint>> Processes::historiesAvailable() const 0500 { 0501 if (!d->mIsLocalHost) 0502 return QList<QPair<QDateTime, uint>>(); 0503 if (!d->mHistoricProcesses) 0504 d->mHistoricProcesses = new ProcessesATop(); 0505 0506 return d->mHistoricProcesses->historiesAvailable(); 0507 } 0508 0509 void Processes::useCurrentData() 0510 { 0511 if (d->mUsingHistoricalData) { 0512 delete d->mHistoricProcesses; 0513 d->mHistoricProcesses = nullptr; 0514 connect(d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated); 0515 d->mUsingHistoricalData = false; 0516 } 0517 } 0518 0519 bool Processes::setViewingTime(const QDateTime &when) 0520 { 0521 if (!d->mIsLocalHost) { 0522 d->mLastError = NotSupported; 0523 return false; 0524 } 0525 if (!d->mUsingHistoricalData) { 0526 if (!d->mHistoricProcesses) 0527 d->mHistoricProcesses = new ProcessesATop(); 0528 disconnect(d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated); 0529 connect(d->mHistoricProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated); 0530 d->mUsingHistoricalData = true; 0531 } 0532 return d->mHistoricProcesses->setViewingTime(when); 0533 } 0534 0535 bool Processes::loadHistoryFile(const QString &filename) 0536 { 0537 if (!d->mIsLocalHost) { 0538 d->mLastError = NotSupported; 0539 return false; 0540 } 0541 if (!d->mHistoricProcesses) 0542 d->mHistoricProcesses = new ProcessesATop(false); 0543 0544 return d->mHistoricProcesses->loadHistoryFile(filename); 0545 } 0546 0547 QString Processes::historyFileName() const 0548 { 0549 if (!d->mIsLocalHost || !d->mHistoricProcesses) 0550 return QString(); 0551 return d->mHistoricProcesses->historyFileName(); 0552 } 0553 QDateTime Processes::viewingTime() const 0554 { 0555 if (!d->mIsLocalHost || !d->mHistoricProcesses) 0556 return QDateTime(); 0557 return d->mHistoricProcesses->viewingTime(); 0558 } 0559 0560 bool Processes::isHistoryAvailable() const 0561 { 0562 if (!d->mIsLocalHost) 0563 return false; 0564 if (!d->mHistoricProcesses) 0565 d->mHistoricProcesses = new ProcessesATop(); 0566 0567 return d->mHistoricProcesses->isHistoryAvailable(); 0568 } 0569 0570 }