1
/* This file is part of the KDE project
3
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License as published by the Free Software Foundation; either
8
version 2 of the License, or (at your option) any later version.
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to
17
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
Boston, MA 02110-1301, USA.
22
#include "processes.h"
23
#include "processes_base_p.h"
24
#include "processes_local_p.h"
25
#include "processes_remote_p.h"
26
#include "processes_atop_p.h"
35
#include <QMutableSetIterator>
41
/* if porting to an OS without signal.h please #define SIGTERM to something */
47
class Processes::Private
50
Private(Processes *q_ptr) {
51
mAbstractProcesses = 0;
52
mHistoricProcesses = 0;
54
mProcesses.insert(-1, &mFakeProcess);
55
mElapsedTimeMilliSeconds = 0;
56
mHavePreviousIoValues = false;
58
mUsingHistoricalData = false;
62
void markProcessesAsEnded(long pid);
64
QSet<long> mToBeProcessed;
65
QSet<long> mProcessedLastTime;
66
QSet<long> mEndedProcesses; ///< Processes that have finished
68
QHash<long, Process *> mProcesses; ///< This must include mFakeProcess at pid -1
69
QList<Process *> mListProcesses; ///< A list of the processes. Does not include mFakeProcesses
70
Process mFakeProcess; ///< A fake process with pid -1 just so that even init points to a parent
72
AbstractProcesses *mAbstractProcesses; ///< The OS specific code to get the process information
73
ProcessesATop *mHistoricProcesses; ///< A way to get historic information about processes
74
bool mIsLocalHost; ///< Whether this is localhost or not
76
QTime mLastUpdated; ///< This is the time we last updated. Used to calculate cpu usage.
77
long mElapsedTimeMilliSeconds; ///< The number of milliseconds (1000ths of a second) that passed since the last update
79
Processes::UpdateFlags mUpdateFlags;
80
bool mHavePreviousIoValues; ///< This is whether we updated the IO value on the last update
81
bool mUsingHistoricalData; ///< Whether to return historical data for updateProcess() etc
85
Processes::Private::~Private() {
86
Q_FOREACH(Process *process, mProcesses) {
87
if(process != &mFakeProcess)
91
mListProcesses.clear();
92
delete mAbstractProcesses;
93
mAbstractProcesses = NULL;
96
Processes::Processes(const QString &host, QObject *parent) : QObject(parent), d(new Private(this))
98
KGlobal::locale()->insertCatalog("processcore"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
100
d->mAbstractProcesses = new ProcessesLocal();
102
ProcessesRemote *remote = new ProcessesRemote(host);
103
d->mAbstractProcesses = remote;
104
connect(remote, SIGNAL(runCommand(QString,int)), this, SIGNAL(runCommand(QString,int)));
106
d->mIsLocalHost = host.isEmpty();
107
connect( d->mAbstractProcesses, SIGNAL(processesUpdated()), SLOT(processesUpdated()));
109
Processes::~Processes()
114
Processes::Error Processes::lastError() const
116
return d->mAbstractProcesses->errorCode;
118
Process *Processes::getProcess(long pid) const
120
return d->mProcesses.value(pid);
123
const QList<Process *> &Processes::getAllProcesses() const
125
return d->mListProcesses;
128
int Processes::processCount() const
130
return d->mListProcesses.count();
133
bool Processes::updateProcess( Process *ps, long ppid)
135
Process *parent = d->mProcesses.value(ppid);
137
parent = &d->mFakeProcess;
138
Q_ASSERT(parent); //even init has a non-null parent - the mFakeProcess
140
if(ps->parent != parent) {
141
emit beginMoveProcess(ps, parent/*new parent*/);
142
//Processes has been reparented
147
} while (p->pid!= -1);
148
Q_ASSERT(ps != parent);
149
ps->parent->children.removeAll(ps);
150
ps->parent = parent; //the parent has changed
151
parent->children.append(ps);
156
} while (p->pid!= -1);
157
emit endMoveProcess();
158
Q_ASSERT(ps != parent);
162
ps->parent_pid = ppid;
164
bool success = updateProcessInfo(ps);
165
emit processChanged(ps, false);
170
bool Processes::updateProcessInfo(Process *ps) {
171
//Now we can actually get the process info
172
qlonglong oldUserTime = ps->userTime;
173
qlonglong oldSysTime = ps->sysTime;
175
qlonglong oldIoCharactersRead = 0;
176
qlonglong oldIoCharactersWritten = 0;
177
qlonglong oldIoReadSyscalls = 0;
178
qlonglong oldIoWriteSyscalls = 0;
179
qlonglong oldIoCharactersActuallyRead = 0;
180
qlonglong oldIoCharactersActuallyWritten = 0;
182
if(d->mUpdateFlags.testFlag(Processes::IOStatistics)) {
183
oldIoCharactersRead = ps->ioCharactersRead;
184
oldIoCharactersWritten = ps->ioCharactersWritten;
185
oldIoReadSyscalls = ps->ioReadSyscalls;
186
oldIoWriteSyscalls = ps->ioWriteSyscalls;
187
oldIoCharactersActuallyRead = ps->ioCharactersActuallyRead;
188
oldIoCharactersActuallyWritten = ps->ioCharactersActuallyWritten;
191
ps->changes = Process::Nothing;
193
if(d->mUsingHistoricalData)
194
success = d->mHistoricProcesses->updateProcessInfo(ps->pid, ps);
196
success = d->mAbstractProcesses->updateProcessInfo(ps->pid, ps);
198
//Now we have the process info. Calculate the cpu usage and total cpu usage for itself and all its parents
199
if(!d->mUsingHistoricalData && d->mElapsedTimeMilliSeconds != 0) { //Update the user usage and sys usage
201
/* The elapsed time is the d->mElapsedTimeMilliSeconds
202
* (which is of the order 2 seconds or so) plus a small
203
* correction where we get the amount of time elapsed since
204
* we start processing. This is because the processing itself
205
* can take a non-trivial amount of time. */
206
int elapsedTime = ps->elapsedTimeMilliSeconds;
207
ps->elapsedTimeMilliSeconds = d->mLastUpdated.elapsed();
208
elapsedTime = ps->elapsedTimeMilliSeconds - elapsedTime + d->mElapsedTimeMilliSeconds;
210
ps->setUserUsage((int)(((ps->userTime - oldUserTime)*1000.0) / elapsedTime));
211
ps->setSysUsage((int)(((ps->sysTime - oldSysTime)*1000.0) / elapsedTime));
214
if(d->mUpdateFlags.testFlag(Processes::IOStatistics)) {
215
if( d->mHavePreviousIoValues ) {
216
ps->setIoCharactersReadRate((ps->ioCharactersRead - oldIoCharactersRead) * 1000.0 / elapsedTime);
217
ps->setIoCharactersWrittenRate((ps->ioCharactersWritten - oldIoCharactersWritten) * 1000.0 / elapsedTime);
218
ps->setIoReadSyscallsRate((ps->ioReadSyscalls - oldIoReadSyscalls) * 1000.0 / elapsedTime);
219
ps->setIoWriteSyscallsRate((ps->ioWriteSyscalls - oldIoWriteSyscalls) * 1000.0 / elapsedTime);
220
ps->setIoCharactersActuallyReadRate((ps->ioCharactersActuallyRead - oldIoCharactersActuallyRead) * 1000.0 / elapsedTime);
221
ps->setIoCharactersActuallyWrittenRate((ps->ioCharactersActuallyWritten - oldIoCharactersActuallyWritten) * 1000.0 / elapsedTime);
223
d->mHavePreviousIoValues = true;
224
} else if(d->mHavePreviousIoValues) {
225
d->mHavePreviousIoValues = false;
226
ps->setIoCharactersReadRate(0);
227
ps->setIoCharactersWrittenRate(0);
228
ps->setIoReadSyscallsRate(0);
229
ps->setIoWriteSyscallsRate(0);
230
ps->setIoCharactersActuallyReadRate(0);
231
ps->setIoCharactersActuallyWrittenRate(0);
234
if(d->mUsingHistoricalData || d->mElapsedTimeMilliSeconds != 0) {
235
ps->setTotalUserUsage(ps->userUsage);
236
ps->setTotalSysUsage(ps->sysUsage);
237
if(ps->userUsage != 0 || ps->sysUsage != 0) {
238
Process *p = ps->parent;
239
while(p->pid != -1) {
240
p->totalUserUsage += ps->userUsage;
241
p->totalSysUsage += ps->sysUsage;
242
emit processChanged(p, true);
251
bool Processes::addProcess(long pid, long ppid)
253
Process *parent = d->mProcesses.value(ppid);
255
//Under race conditions, the parent could have already quit
256
//In this case, attach to top leaf
257
parent = &d->mFakeProcess;
258
Q_ASSERT(parent); //even init has a non-null parent - the mFakeProcess
260
//it's a new process - we need to set it up
261
Process *ps = new Process(pid, ppid, parent);
263
emit beginAddProcess(ps);
265
d->mProcesses.insert(pid, ps);
267
ps->index = d->mListProcesses.count();
268
d->mListProcesses.append(ps);
270
ps->parent->children.append(ps);
276
} while (p->pid != -1);
277
ps->parent_pid = ppid;
279
//Now we can actually get the process info
280
bool success = updateProcessInfo(ps);
281
emit endAddProcess();
285
bool Processes::updateOrAddProcess( long pid)
288
if(d->mUsingHistoricalData)
289
ppid = d->mHistoricProcesses->getParentPid(pid);
291
ppid = d->mAbstractProcesses->getParentPid(pid);
293
if (ppid == pid) //Shouldn't ever happen
296
if(d->mToBeProcessed.contains(ppid)) {
297
//Make sure that we update the parent before we update this one. Just makes things a bit easier.
298
d->mToBeProcessed.remove(ppid);
299
d->mProcessedLastTime.remove(ppid); //It may or may not be here - remove it if it is there
300
updateOrAddProcess(ppid);
303
Process *ps = d->mProcesses.value(pid);
305
return addProcess(pid, ppid);
307
return updateProcess(ps, ppid);
310
void Processes::updateAllProcesses(long updateDurationMS, Processes::UpdateFlags updateFlags)
312
d->mUpdateFlags = updateFlags;
314
if(d->mUsingHistoricalData || d->mLastUpdated.elapsed() >= updateDurationMS || !d->mLastUpdated.isValid()) {
315
d->mElapsedTimeMilliSeconds = d->mLastUpdated.restart();
316
if(d->mUsingHistoricalData)
317
d->mHistoricProcesses->updateAllProcesses(d->mUpdateFlags);
319
d->mAbstractProcesses->updateAllProcesses(d->mUpdateFlags); //For a local machine, this will directly call Processes::processesUpdated()
323
void Processes::processesUpdated() {
324
//First really delete any processes that ended last time
327
QSetIterator<long> i(d->mEndedProcesses);
328
while( i.hasNext() ) {
334
if(d->mUsingHistoricalData)
335
d->mToBeProcessed = d->mHistoricProcesses->getAllPids();
337
d->mToBeProcessed = d->mAbstractProcesses->getAllPids();
340
QSet<long> beingProcessed(d->mToBeProcessed); //keep a copy so that we can replace mProcessedLastTime with this at the end of this function
343
QMutableSetIterator<long> i(d->mToBeProcessed);
344
while( i.hasNext()) {
347
d->mProcessedLastTime.remove(pid); //It may or may not be here - remove it if it is there
348
updateOrAddProcess(pid); //This adds the process or changes an existing one
349
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
353
QSetIterator<long> i(d->mProcessedLastTime);
354
while( i.hasNext()) {
355
//We saw these pids last time, but not this time. That means we have to mark them for deletion now
357
d->markProcessesAsEnded(pid);
359
d->mEndedProcesses = d->mProcessedLastTime;
362
d->mProcessedLastTime = beingProcessed; //update the set for next time this function is called
366
void Processes::Private::markProcessesAsEnded(long pid)
370
Process *process = mProcesses.value(pid);
373
process->status = Process::Ended;
374
emit q->processChanged(process, false);
376
void Processes::deleteProcess(long pid)
380
Process *process = d->mProcesses.value(pid);
383
Q_FOREACH( Process *it, process->children) {
384
d->mProcessedLastTime.remove(it->pid);
385
deleteProcess(it->pid);
388
emit beginRemoveProcess(process);
390
d->mProcesses.remove(pid);
391
d->mListProcesses.removeAll(process);
392
process->parent->children.removeAll(process);
393
Process *p = process;
398
} while (p->pid != -1);
402
Q_FOREACH( Process *it, d->mListProcesses ) {
403
if(it->index > process->index)
405
Q_ASSERT(it->index == i++);
409
emit endRemoveProcess();
413
bool Processes::killProcess(long pid) {
414
return sendSignal(pid, SIGTERM);
417
bool Processes::sendSignal(long pid, int sig) {
418
d->mAbstractProcesses->errorCode = Unknown;
419
if(d->mUsingHistoricalData) {
420
d->mAbstractProcesses->errorCode = NotSupported;
423
return d->mAbstractProcesses->sendSignal(pid, sig);
426
bool Processes::setNiceness(long pid, int priority) {
427
d->mAbstractProcesses->errorCode = Unknown;
428
if(d->mUsingHistoricalData) {
429
d->mAbstractProcesses->errorCode = NotSupported;
432
return d->mAbstractProcesses->setNiceness(pid, priority);
435
bool Processes::setScheduler(long pid, KSysGuard::Process::Scheduler priorityClass, int priority) {
436
d->mAbstractProcesses->errorCode = Unknown;
437
if(d->mUsingHistoricalData) {
438
d->mAbstractProcesses->errorCode = NotSupported;
441
return d->mAbstractProcesses->setScheduler(pid, priorityClass, priority);
444
bool Processes::setIoNiceness(long pid, KSysGuard::Process::IoPriorityClass priorityClass, int priority) {
445
d->mAbstractProcesses->errorCode = Unknown;
446
if(d->mUsingHistoricalData) {
447
d->mAbstractProcesses->errorCode = NotSupported;
450
return d->mAbstractProcesses->setIoNiceness(pid, priorityClass, priority);
453
bool Processes::supportsIoNiceness() {
454
if(d->mUsingHistoricalData)
456
return d->mAbstractProcesses->supportsIoNiceness();
459
long long Processes::totalPhysicalMemory() {
460
return d->mAbstractProcesses->totalPhysicalMemory();
463
long Processes::numberProcessorCores() {
464
return d->mAbstractProcesses->numberProcessorCores();
467
void Processes::answerReceived( int id, const QList<QByteArray>& answer ) {
468
KSysGuard::ProcessesRemote *processes = qobject_cast<KSysGuard::ProcessesRemote *>(d->mAbstractProcesses);
470
processes->answerReceived(id, answer);
473
QList< QPair<QDateTime,uint> > Processes::historiesAvailable() const
476
return QList< QPair<QDateTime,uint> >();
477
if(!d->mHistoricProcesses)
478
d->mHistoricProcesses = new ProcessesATop();
480
return d->mHistoricProcesses->historiesAvailable();
483
void Processes::useCurrentData()
485
if(d->mUsingHistoricalData) {
486
delete d->mHistoricProcesses;
487
d->mHistoricProcesses = NULL;
488
connect( d->mAbstractProcesses, SIGNAL(processesUpdated()), SLOT(processesUpdated()));
489
d->mUsingHistoricalData = false;
493
bool Processes::setViewingTime(const QDateTime &when)
495
d->mAbstractProcesses->errorCode = Unknown;
496
if(!d->mIsLocalHost) {
497
d->mAbstractProcesses->errorCode = NotSupported;
500
if(!d->mUsingHistoricalData) {
501
if(!d->mHistoricProcesses)
502
d->mHistoricProcesses = new ProcessesATop();
503
disconnect( d->mAbstractProcesses, SIGNAL(processesUpdated()), this, SLOT(processesUpdated()));
504
connect( d->mHistoricProcesses, SIGNAL(processesUpdated()), SLOT(processesUpdated()));
505
d->mUsingHistoricalData = true;
507
return d->mHistoricProcesses->setViewingTime(when);
510
bool Processes::loadHistoryFile(const QString &filename)
512
d->mAbstractProcesses->errorCode = Unknown;
513
if(!d->mIsLocalHost) {
514
d->mAbstractProcesses->errorCode = NotSupported;
517
if(!d->mHistoricProcesses)
518
d->mHistoricProcesses = new ProcessesATop(false);
520
return d->mHistoricProcesses->loadHistoryFile(filename);
523
QString Processes::historyFileName() const
525
if(!d->mIsLocalHost || !d->mHistoricProcesses)
526
return QString::null;
527
return d->mHistoricProcesses->historyFileName();
529
QDateTime Processes::viewingTime() const
531
if(!d->mIsLocalHost || !d->mHistoricProcesses)
533
return d->mHistoricProcesses->viewingTime();
536
bool Processes::isHistoryAvailable() const
540
if(!d->mHistoricProcesses)
541
d->mHistoricProcesses = new ProcessesATop();
543
return d->mHistoricProcesses->isHistoryAvailable();
547
#include "processes.moc"