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