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_local_p.h"
31
#include <QTextStream>
35
//for kill and setNice
37
#include <sys/types.h>
39
#include <sys/resource.h>
43
#include <sys/ptrace.h>
44
#include <asm/unistd.h>
48
#define PROCESS_BUFFER_SIZE 1000
51
extern int sys_ioprio_set(int, int, int);
52
extern int sys_ioprio_get(int, int);
55
/* Check if this system has ionice */
56
#if !defined(SYS_ioprio_get) || !defined(SYS_ioprio_set)
57
/* All new kernels have SYS_ioprio_get and _set defined, but for the few that do not, here are the definitions */
59
#define __NR_ioprio_set 289
60
#define __NR_ioprio_get 290
61
#elif defined(__ppc__) || defined(__powerpc__)
62
#define __NR_ioprio_set 273
63
#define __NR_ioprio_get 274
64
#elif defined(__x86_64__)
65
#define __NR_ioprio_set 251
66
#define __NR_ioprio_get 252
67
#elif defined(__ia64__)
68
#define __NR_ioprio_set 1274
69
#define __NR_ioprio_get 1275
72
#warning "This architecture does not support IONICE. Disabling ionice feature."
76
/* Map these to SYS_ioprio_get */
77
#define SYS_ioprio_get __NR_ioprio_get
78
#define SYS_ioprio_set __NR_ioprio_set
80
#endif /* !SYS_ioprio_get */
82
/* Set up ionice functions */
84
#define IOPRIO_WHO_PROCESS 1
85
#define IOPRIO_CLASS_SHIFT 13
87
/* Expose the kernel calls to userspace via syscall
88
* See man ioprio_set and man ioprio_get for information on these functions */
89
static int ioprio_set(int which, int who, int ioprio)
91
return syscall(SYS_ioprio_set, which, who, ioprio);
94
static int ioprio_get(int which, int who)
96
return syscall(SYS_ioprio_get, which, who);
106
class ProcessesLocal::Private
109
Private() { mProcDir = opendir( "/proc" );}
111
inline bool readProcStatus(const QString &dir, Process *process);
112
inline bool readProcStat(const QString &dir, Process *process);
113
inline bool readProcStatm(const QString &dir, Process *process);
114
inline bool readProcCmdline(const QString &dir, Process *process);
115
inline bool getNiceness(long pid, Process *process);
116
inline bool getIOStatistics(const QString &dir, Process *process);
118
char mBuffer[PROCESS_BUFFER_SIZE+1]; //used as a buffer to read data into
122
ProcessesLocal::Private::~Private()
127
ProcessesLocal::ProcessesLocal() : d(new Private())
132
bool ProcessesLocal::Private::readProcStatus(const QString &dir, Process *process)
134
mFile.setFileName(dir + "status");
135
if(!mFile.open(QIODevice::ReadOnly))
136
return false; /* process has terminated in the meantime */
140
process->tracerpid = -1;
141
process->numThreads = 0;
144
int found = 0; //count how many fields we found
145
while( (size = mFile.readLine( mBuffer, sizeof(mBuffer))) > 0) { //-1 indicates an error
146
switch( mBuffer[0]) {
148
if((unsigned int)size > sizeof("Name:") && qstrncmp(mBuffer, "Name:", sizeof("Name:")-1) == 0) {
149
if(process->command.isEmpty())
150
process->name = QString::fromLocal8Bit(mBuffer + sizeof("Name:")-1, size-sizeof("Name:")+1).trimmed();
151
if(++found == 5) goto finish;
155
if((unsigned int)size > sizeof("Uid:") && qstrncmp(mBuffer, "Uid:", sizeof("Uid:")-1) == 0) {
156
sscanf(mBuffer + sizeof("Uid:") -1, "%Ld %Ld %Ld %Ld", &process->uid, &process->euid, &process->suid, &process->fsuid );
157
if(++found == 5) goto finish;
161
if((unsigned int)size > sizeof("Gid:") && qstrncmp(mBuffer, "Gid:", sizeof("Gid:")-1) == 0) {
162
sscanf(mBuffer + sizeof("Gid:")-1, "%Ld %Ld %Ld %Ld", &process->gid, &process->egid, &process->sgid, &process->fsgid );
163
if(++found == 5) goto finish;
167
if((unsigned int)size > sizeof("TracerPid:") && qstrncmp(mBuffer, "TracerPid:", sizeof("TracerPid:")-1) == 0) {
168
process->tracerpid = atol(mBuffer + sizeof("TracerPid:")-1);
169
if (process->tracerpid == 0)
170
process->tracerpid = -1;
171
if(++found == 5) goto finish;
172
} else if((unsigned int)size > sizeof("Threads:") && qstrncmp(mBuffer, "Threads:", sizeof("Threads:")-1) == 0) {
173
process->setNumThreads(atol(mBuffer + sizeof("Threads:")-1));
174
if(++found == 5) goto finish;
187
long ProcessesLocal::getParentPid(long pid) {
189
d->mFile.setFileName("/proc/" + QString::number(pid) + "/stat");
190
if(!d->mFile.open(QIODevice::ReadOnly))
191
return -1; /* process has terminated in the meantime */
193
int size; //amount of data read in
194
if( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) <= 0) { //-1 indicates nothing read
200
int current_word = 0;
201
char *word = d->mBuffer;
204
if(word[0] == ' ' ) {
205
if(++current_word == 3)
207
} else if(word[0] == 0) {
208
return -1; //end of data - serious problem
212
long ppid = atol(++word);
218
bool ProcessesLocal::Private::readProcStat(const QString &dir, Process *ps)
220
QString filename = dir + "stat";
221
// As an optimization, if the last file read in was stat, then we already have this info in memory
222
if(mFile.fileName() != filename) {
223
mFile.setFileName(filename);
224
if(!mFile.open(QIODevice::ReadOnly))
225
return false; /* process has terminated in the meantime */
226
if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
233
int current_word = 0; //count from 0
234
char *word = mBuffer;
236
unsigned long long vmSize = 0;
237
unsigned long long vmRSS = 0;
238
while(current_word < 23) {
239
if(word[0] == ' ' ) {
241
switch(current_word) {
243
status=word[1]; // Look at the first letter of the status.
244
// We analyze this after the while loop
248
int ttyNo = atoi(word+1);
249
int major = ttyNo >> 8;
250
int minor = ttyNo & 0xff;
253
ps->setTty(QByteArray("pts/") + QByteArray::number(minor));
256
ps->setTty(QByteArray("tty"));
259
ps->setTty(QByteArray("tty") + QByteArray::number(minor));
261
ps->setTty(QByteArray("ttyS") + QByteArray::number(minor-64));
264
ps->setTty(QByteArray());
269
ps->setUserTime(atoll(word+1));
272
ps->setSysTime(atoll(word+1));
275
ps->setNiceLevel(atoi(word+1)); /*Or should we use getPriority instead? */
278
vmSize = atoll(word+1);
281
vmRSS = atoll(word+1);
286
} else if(word[0] == 0) {
287
return false; //end of data - serious problem
292
/* There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here in the original ksysguard code. I have no idea why! After comparing it to
293
* meminfo and other tools, this means we report the RSS by 12 bytes differently compared to them. So I'm removing the +3
294
* to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-)
296
* Update: I think I now know why - the kernel allocates 3 pages for
297
* tracking information about each the process. This memory isn't
298
* included in vmRSS..*/
299
ps->setVmRSS(vmRSS * (sysconf(_SC_PAGESIZE) / 1024)); /*convert to KiB*/
300
ps->setVmSize(vmSize / 1024); /* convert to KiB */
304
ps->setStatus(Process::Running);
307
ps->setStatus(Process::Sleeping);
310
ps->setStatus(Process::DiskSleep);
313
ps->setStatus(Process::Zombie);
316
ps->setStatus(Process::Stopped);
319
ps->setStatus(Process::Paging);
322
ps->setStatus(Process::OtherStatus);
328
bool ProcessesLocal::Private::readProcStatm(const QString &dir, Process *process)
331
mFile.setFileName(dir + "statm");
332
if(!mFile.open(QIODevice::ReadOnly))
333
return false; /* process has terminated in the meantime */
335
if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
341
int current_word = 0;
342
char *word = mBuffer;
345
if(word[0] == ' ' ) {
346
if(++current_word == 2) //number of pages that are shared
348
} else if(word[0] == 0) {
349
return false; //end of data - serious problem
353
long shared = atol(word+1);
355
/* we use the rss - shared to find the amount of memory just this app uses */
356
process->vmURSS = process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024);
364
bool ProcessesLocal::Private::readProcCmdline(const QString &dir, Process *process)
366
if(!process->command.isNull()) return true; //only parse the cmdline once. This function takes up 25% of the CPU time :-/
367
mFile.setFileName(dir + "cmdline");
368
if(!mFile.open(QIODevice::ReadOnly))
369
return false; /* process has terminated in the meantime */
371
QTextStream in(&mFile);
372
process->command = in.readAll();
374
//cmdline separates parameters with the NULL character
375
if(!process->command.isEmpty()) {
376
if(process->command.startsWith(process->name)) {
377
int index = process->command.indexOf(QChar('\0'));
378
process->name = process->command.left(index);
380
process->command.replace('\0', ' ');
387
bool ProcessesLocal::Private::getNiceness(long pid, Process *process) {
388
int sched = sched_getscheduler(pid);
391
process->scheduler = KSysGuard::Process::Other;
394
process->scheduler = KSysGuard::Process::RoundRobin;
397
process->scheduler = KSysGuard::Process::Fifo;
401
process->scheduler = KSysGuard::Process::SchedulerIdle;
405
process->scheduler = KSysGuard::Process::Batch;
409
process->scheduler = KSysGuard::Process::Other;
411
if(sched == SCHED_FIFO || sched == SCHED_RR) {
412
struct sched_param param;
413
if(sched_getparam(pid, ¶m) == 0)
414
process->setNiceLevel(param.sched_priority);
416
process->setNiceLevel(0); //Error getting scheduler parameters.
420
int ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); /* Returns from 0 to 7 for the iopriority, and -1 if there's an error */
422
process->ioniceLevel = -1;
423
process->ioPriorityClass = KSysGuard::Process::None;
424
return false; /* Error. Just give up. */
426
process->ioniceLevel = ioprio & 0xff; /* Bottom few bits are the priority */
427
process->ioPriorityClass = (KSysGuard::Process::IoPriorityClass)(ioprio >> IOPRIO_CLASS_SHIFT); /* Top few bits are the class */
430
return false; /* Do nothing, if we do not support this architecture */
434
bool ProcessesLocal::Private::getIOStatistics(const QString &dir, Process *process)
436
QString filename = dir + "io";
437
// As an optimization, if the last file read in was io, then we already have this info in memory
438
mFile.setFileName(filename);
439
if(!mFile.open(QIODevice::ReadOnly))
440
return false; /* process has terminated in the meantime */
441
if( mFile.read( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
447
int current_word = 0; //count from 0
448
char *word = mBuffer;
449
while(current_word < 6 && word[0] != 0) {
450
if(word[0] == ' ' ) {
451
qlonglong number = atoll(word+1);
452
switch(current_word++) {
453
case 0: //rchar - characters read
454
process->setIoCharactersRead(number);
456
case 1: //wchar - characters written
457
process->setIoCharactersWritten(number);
459
case 2: //syscr - read syscall
460
process->setIoReadSyscalls(number);
462
case 3: //syscw - write syscall
463
process->setIoWriteSyscalls(number);
465
case 4: //read_bytes - bytes actually read from I/O
466
process->setIoCharactersActuallyRead(number);
468
case 5: //write_bytes - bytes actually written to I/O
469
process->setIoCharactersActuallyWritten(number);
478
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
481
QString dir = "/proc/" + QString::number(pid) + '/';
482
if(!d->readProcStat(dir, process)) success = false;
483
if(!d->readProcStatus(dir, process)) success = false;
484
if(!d->readProcStatm(dir, process)) success = false;
485
if(!d->readProcCmdline(dir, process)) success = false;
486
if(!d->getNiceness(pid, process)) success = false;
487
if(mUpdateFlags.testFlag(Processes::IOStatistics) && !d->getIOStatistics(dir, process)) success = false;
492
QSet<long> ProcessesLocal::getAllPids( )
495
if(d->mProcDir==NULL) return pids; //There's not much we can do without /proc
496
struct dirent* entry;
497
rewinddir(d->mProcDir);
498
while ( ( entry = readdir( d->mProcDir ) ) )
499
if ( entry->d_name[ 0 ] >= '0' && entry->d_name[ 0 ] <= '9' )
500
pids.insert(atol( entry->d_name ));
504
bool ProcessesLocal::sendSignal(long pid, int sig) {
507
errorCode = Processes::InvalidPid;
510
if (kill( (pid_t)pid, sig )) {
513
errorCode = Processes::ProcessDoesNotExistOrZombie;
516
errorCode = Processes::InvalidParameter;
519
errorCode = Processes::InsufficientPermissions;
530
bool ProcessesLocal::setNiceness(long pid, int priority) {
533
errorCode = Processes::InvalidPid;
536
if (setpriority( PRIO_PROCESS, pid, priority )) {
539
errorCode = Processes::ProcessDoesNotExistOrZombie;
542
errorCode = Processes::InvalidParameter;
546
errorCode = Processes::InsufficientPermissions;
551
//set niceness failed
557
bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority) {
559
if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch || priorityClass == KSysGuard::Process::SchedulerIdle)
562
errorCode = Processes::InvalidPid;
565
struct sched_param params;
566
params.sched_priority = priority;
568
switch(priorityClass) {
569
case (KSysGuard::Process::Other):
570
policy = SCHED_OTHER;
572
case (KSysGuard::Process::RoundRobin):
575
case (KSysGuard::Process::Fifo):
579
case (KSysGuard::Process::SchedulerIdle):
584
case (KSysGuard::Process::Batch):
585
policy = SCHED_BATCH;
589
errorCode = Processes::NotSupported;
593
if (sched_setscheduler( pid, policy, ¶ms) != 0) {
596
errorCode = Processes::ProcessDoesNotExistOrZombie;
599
errorCode = Processes::InvalidParameter;
602
errorCode = Processes::InsufficientPermissions;
613
bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
616
errorCode = Processes::InvalidPid;
620
if (ioprio_set(IOPRIO_WHO_PROCESS, pid, priority | priorityClass << IOPRIO_CLASS_SHIFT) == -1) {
621
//set io niceness failed
624
errorCode = Processes::ProcessDoesNotExistOrZombie;
627
errorCode = Processes::InvalidParameter;
630
errorCode = Processes::InsufficientPermissions;
639
errorCode = Processes::NotSupported;
644
bool ProcessesLocal::supportsIoNiceness() {
652
long long ProcessesLocal::totalPhysicalMemory() {
653
//Try to get the memory via sysconf. Note the cast to long long to try to avoid a long overflow
654
//Should we use sysconf(_SC_PAGESIZE) or getpagesize() ?
655
#ifdef _SC_PHYS_PAGES
656
return ((long long)sysconf(_SC_PHYS_PAGES)) * (sysconf(_SC_PAGESIZE)/1024);
658
//This is backup code in case this is not defined. It should never fail on a linux system.
660
d->mFile.setFileName("/proc/meminfo");
661
if(!d->mFile.open(QIODevice::ReadOnly))
665
while( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) > 0) { //-1 indicates an error
666
switch( d->mBuffer[0]) {
668
if((unsigned int)size > sizeof("MemTotal:") && qstrncmp(d->mBuffer, "MemTotal:", sizeof("MemTotal:")-1) == 0) {
670
return atoll(d->mBuffer + sizeof("MemTotal:")-1);
674
return 0; // Not found. Probably will never happen
677
ProcessesLocal::~ProcessesLocal()