LinuxProcessList.c 23.6 KB
Newer Older
1
2
3
4
5
6
7
/*
htop - LinuxProcessList.c
(C) 2014 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

Hisham Muhammad's avatar
Hisham Muhammad committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "LinuxProcessList.h"
#include "LinuxProcess.h"
#include "CRT.h"
#include "String.h"
#include <errno.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <sys/types.h>
#include <fcntl.h>
29
30
31

/*{

Hisham Muhammad's avatar
Hisham Muhammad committed
32
33
#include "ProcessList.h"

Hisham Muhammad's avatar
Hisham Muhammad committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
typedef struct CPUData_ {
   unsigned long long int totalTime;
   unsigned long long int userTime;
   unsigned long long int systemTime;
   unsigned long long int systemAllTime;
   unsigned long long int idleAllTime;
   unsigned long long int idleTime;
   unsigned long long int niceTime;
   unsigned long long int ioWaitTime;
   unsigned long long int irqTime;
   unsigned long long int softIrqTime;
   unsigned long long int stealTime;
   unsigned long long int guestTime;
   
   unsigned long long int totalPeriod;
   unsigned long long int userPeriod;
   unsigned long long int systemPeriod;
   unsigned long long int systemAllPeriod;
   unsigned long long int idleAllPeriod;
   unsigned long long int idlePeriod;
   unsigned long long int nicePeriod;
   unsigned long long int ioWaitPeriod;
   unsigned long long int irqPeriod;
   unsigned long long int softIrqPeriod;
   unsigned long long int stealPeriod;
   unsigned long long int guestPeriod;
} CPUData;

typedef struct LinuxProcessList_ {
   ProcessList super;

   CPUData* cpus;

} LinuxProcessList;

69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif

#ifndef PROCSTATFILE
#define PROCSTATFILE PROCDIR "/stat"
#endif

#ifndef PROCMEMINFOFILE
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif

}*/
   
Hisham Muhammad's avatar
Hisham Muhammad committed
83
84
85
86
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
   LinuxProcessList* this = calloc(1, sizeof(LinuxProcessList));
   ProcessList* pl = &(this->super);
   ProcessList_init(pl, usersTable, pidWhiteList, userId);
87
88
89
90
91
92
93
94
95
96
97
98
99
100

   // Update CPU count:
   FILE* file = fopen(PROCSTATFILE, "r");
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
   char buffer[256];
   int cpus = -1;
   do {
      cpus++;
      fgets(buffer, 255, file);
   } while (String_startsWith(buffer, "cpu"));
   fclose(file);

Hisham Muhammad's avatar
Hisham Muhammad committed
101
   pl->cpuCount = MAX(cpus - 1, 1);
102
   this->cpus = calloc(cpus, sizeof(CPUData));
103
104
105
106
107
108
109

   for (int i = 0; i < cpus; i++) {
      this->cpus[i].totalTime = 1;
      this->cpus[i].totalPeriod = 1;
   }

   #ifdef HAVE_OPENVZ
110
   this->flags |= PROCESS_FLAG_LINUX_OPENVZ;
111
112
   #endif

Hisham Muhammad's avatar
Hisham Muhammad committed
113
   return pl;
114
115
}

Hisham Muhammad's avatar
Hisham Muhammad committed
116
117
118
119
void ProcessList_delete(ProcessList* pl) {
   LinuxProcessList* this = (LinuxProcessList*) pl;
   ProcessList_done(pl);
   free(this->cpus);
120
121
122
   free(this);
}

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
static ssize_t xread(int fd, void *buf, size_t count) {
  // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
  size_t alreadyRead = 0;
  for(;;) {
     ssize_t res = read(fd, buf, count);
     if (res == -1 && errno == EINTR) continue;
     if (res > 0) {
       buf = ((char*)buf)+res;
       count -= res;
       alreadyRead += res;
     }
     if (res == -1) return -1;
     if (count == 0 || res == 0) return alreadyRead;
  }
}

139
140
141
142
143
144
145
146
static double jiffy = 0.0;

static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
   if(jiffy == 0.0) jiffy = sysconf(_SC_CLK_TCK);
   double jiffytime = 1.0 / jiffy;
   return (unsigned long long) t * jiffytime * 100;
}

147
static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command) {
148
   LinuxProcess* lp = (LinuxProcess*) process;
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
      return false;

   static char buf[MAX_READ+1];

   int size = xread(fd, buf, MAX_READ);
   close(fd);
   if (size <= 0) return false;
   buf[size] = '\0';

   assert(process->pid == atoi(buf));
   char *location = strchr(buf, ' ');
   if (!location) return false;

   location += 2;
   char *end = strrchr(location, ')');
   if (!end) return false;
   
   int commsize = end - location;
   memcpy(command, location, commsize);
   command[commsize] = '\0';
   location = end + 2;

   process->state = location[0];
   location += 2;
   process->ppid = strtol(location, &location, 10);
   location += 1;
   process->pgrp = strtoul(location, &location, 10);
   location += 1;
   process->session = strtoul(location, &location, 10);
   location += 1;
   process->tty_nr = strtoul(location, &location, 10);
   location += 1;
   process->tpgid = strtol(location, &location, 10);
   location += 1;
   process->flags = strtoul(location, &location, 10);
   location += 1;
   process->minflt = strtoull(location, &location, 10);
   location += 1;
191
   lp->cminflt = strtoull(location, &location, 10);
192
193
194
   location += 1;
   process->majflt = strtoull(location, &location, 10);
   location += 1;
195
   lp->cmajflt = strtoull(location, &location, 10);
196
   location += 1;
197
   lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
198
   location += 1;
199
   lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
200
   location += 1;
201
   lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
202
   location += 1;
203
   lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
204
205
206
207
208
209
210
211
212
213
214
   location += 1;
   process->priority = strtol(location, &location, 10);
   location += 1;
   process->nice = strtol(location, &location, 10);
   location += 1;
   process->nlwp = strtol(location, &location, 10);
   location += 1;
   for (int i=0; i<17; i++) location = strchr(location, ' ')+1;
   process->exit_signal = strtol(location, &location, 10);
   location += 1;
   assert(location != NULL);
215
   process->processor = strtol(location, &location, 10);
216
217
   
   process->time = lp->utime + lp->stime;
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

   return true;
}


static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';

   snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
   struct stat sstat;
   int statok = stat(filename, &sstat);
   if (statok == -1)
      return false;
   process->st_uid = sstat.st_uid;
  
   struct tm date;
   time_t ctime = sstat.st_ctime;
   process->starttime_ctime = ctime;
   (void) localtime_r((time_t*) &ctime, &date);
   strftime(process->starttime_show, 7, ((ctime > curTime - 86400) ? "%R " : "%b%d "), &date);
   
   return true;
}

#ifdef HAVE_TASKSTATS

245
static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirname, char* name, unsigned long long now) {
246
247
248
249
250
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';

   snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
   int fd = open(filename, O_RDONLY);
Hisham Muhammad's avatar
Hisham Muhammad committed
251
252
253
   if (fd == -1) {
      process->io_rate_read_bps = -1;
      process->io_rate_write_bps = -1;
254
      return;
Hisham Muhammad's avatar
Hisham Muhammad committed
255
   }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
   
   char buffer[1024];
   ssize_t buflen = xread(fd, buffer, 1023);
   close(fd);
   if (buflen < 1) return;
   buffer[buflen] = '\0';
   unsigned long long last_read = process->io_read_bytes;
   unsigned long long last_write = process->io_write_bytes;
   char *buf = buffer;
   char *line = NULL;
   while ((line = strsep(&buf, "\n")) != NULL) {
      switch (line[0]) {
      case 'r':
         if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
            process->io_rchar = strtoull(line+7, NULL, 10);
         else if (strncmp(line+1, "ead_bytes: ", 11) == 0) {
            process->io_read_bytes = strtoull(line+12, NULL, 10);
            process->io_rate_read_bps = 
               ((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000);
            process->io_rate_read_time = now;
         }
         break;
      case 'w':
         if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
            process->io_wchar = strtoull(line+7, NULL, 10);
         else if (strncmp(line+1, "rite_bytes: ", 12) == 0) {
            process->io_write_bytes = strtoull(line+13, NULL, 10);
            process->io_rate_write_bps = 
               ((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000);
            process->io_rate_write_time = now;
         }
         break;
      case 's':
         if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0)
            process->io_syscr = strtoull(line+7, NULL, 10);
         else if (strncmp(line+1, "yscw: ", 6) == 0)
            sscanf(line, "syscw: %32llu", &process->io_syscw);
            process->io_syscw = strtoull(line+7, NULL, 10);
         break;
      case 'c':
         if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0)
           process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
      }
   }
}

#endif



306
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
307
308
309
310
311
312
313
314
315
316
317
318
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
      return false;
   char buf[256];
   ssize_t rres = xread(fd, buf, 255);
   close(fd);
   if (rres < 1) return false;

   char *p = buf;
   errno = 0;
319
320
   process->super.m_size = strtol(p, &p, 10); if (*p == ' ') p++;
   process->super.m_resident = strtol(p, &p, 10); if (*p == ' ') p++;
321
322
323
324
325
326
327
328
329
330
331
   process->m_share = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_trs = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_lrs = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_drs = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_dt = strtol(p, &p, 10);
   return (errno == 0);
}

#ifdef HAVE_OPENVZ

static void LinuxProcessList_readOpenVZData(ProcessList* this, Process* process, const char* dirname, const char* name) {
332
   if ( (!(this->flags & PROCESS_FLAG_LINUX_OPENVZ)) || (access("/proc/vz", R_OK) != 0)) {
333
334
      process->vpid = process->pid;
      process->ctid = 0;
335
      this->flags |= ~PROCESS_FLAG_LINUX_OPENVZ;
336
337
338
339
340
341
342
      return;
   }
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file) 
      return;
343
   (void) fscanf(file, 
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
      "%*32u %*32s %*1c %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %32u %32u",
      &process->vpid, &process->ctid);
   fclose(file);
}

#endif

#ifdef HAVE_CGROUP

359
static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
360
361
362
363
364
365
366
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file) {
      process->cgroup = strdup("");
      return;
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
   char output[256];
   output[0] = '\0';
   char* at = output;
   int left = 255;
   while (!feof(file) && left > 0) {
      char buffer[256];
      char *ok = fgets(buffer, 255, file);
      if (!ok) break;
      char* group = strchr(buffer, ':');
      if (!group) break;
      if (at != output) {
         *at = ';';
         at++;
         left--;
381
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
382
383
      int wrote = snprintf(at, left, "%s", group);
      left -= wrote;
384
385
   }
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
386
387
   free(process->cgroup);
   process->cgroup = strdup(output);
388
389
390
391
392
393
}

#endif

#ifdef HAVE_VSERVER

394
static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return;
   char buffer[256];
   process->vxid = 0;
   while (fgets(buffer, 255, file)) {
      if (String_startsWith(buffer, "VxID:")) {
         int vxid;
         int ok = sscanf(buffer, "VxID:\t%32d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
         }
      }
      #if defined HAVE_ANCIENT_VSERVER
      else if (String_startsWith(buffer, "s_context:")) {
         int vxid;
         int ok = sscanf(buffer, "s_context:\t%32d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
         }
      }
      #endif
   }
   fclose(file);
}

#endif

#ifdef HAVE_OOM

427
static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return;
   char buffer[256];
   if (fgets(buffer, 255, file)) {
      unsigned int oom;
      int ok = sscanf(buffer, "%32u", &oom);
      if (ok >= 1) {
         process->oom = oom;
      }
   }
   fclose(file);
}

#endif

static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
   if (Process_isKernelThread(process))
      return true;

   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
      return false;
         
   char command[4096+1]; // max cmdline length on Linux
   int amtRead = xread(fd, command, sizeof(command) - 1);
   close(fd);
   int tokenEnd = 0; 
   if (amtRead > 0) {
      for (int i = 0; i < amtRead; i++)
         if (command[i] == '\0' || command[i] == '\n') {
            if (tokenEnd == 0) {
               tokenEnd = i;
            }
            command[i] = ' ';
         }
   }
   if (tokenEnd == 0) {
      tokenEnd = amtRead;
   }
   command[amtRead] = '\0';
   free(process->comm);
   process->comm = strdup(command);
   process->basenameOffset = tokenEnd;

   return true;
}

480
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
Hisham Muhammad's avatar
Hisham Muhammad committed
481
   ProcessList* pl = (ProcessList*) this;
482
483
   DIR* dir;
   struct dirent* entry;
484
   Settings* settings = pl->settings;
485
486
487
488
489
490
491
492

   time_t curTime = tv.tv_sec;
   #ifdef HAVE_TASKSTATS
   unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
   #endif

   dir = opendir(dirname);
   if (!dir) return false;
493
   int cpus = pl->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
494
495
   bool hideKernelThreads = settings->hideKernelThreads;
   bool hideUserlandThreads = settings->hideUserlandThreads;
496
497
498
499
500
   while ((entry = readdir(dir)) != NULL) {
      char* name = entry->d_name;

      // The RedHat kernel hides threads with a dot.
      // I believe this is non-standard.
Hisham Muhammad's avatar
Hisham Muhammad committed
501
      if ((!settings->hideThreads) && name[0] == '.') {
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
         name++;
      }

      // Just skip all non-number directories.
      if (name[0] < '0' || name[0] > '9') {
         continue;
      }

      // filename is a number: process directory
      int pid = atoi(name);
     
      if (parent && pid == parent->pid)
         continue;

      if (pid <= 0) 
         continue;

519
520
521
      bool preExisting = false;
      Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_new_fn) LinuxProcess_new);
      proc->tgid = parent ? parent->pid : pid;
522
523
      
      LinuxProcess* lp = (LinuxProcess*) proc;
524
525
526

      char subdirname[MAX_NAME+1];
      snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
527
      LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
528
529

      #ifdef HAVE_TASKSTATS
Hisham Muhammad's avatar
Hisham Muhammad committed
530
      if (settings->flags & PROCESS_FLAG_IO)
531
         LinuxProcessList_readIoFile(lp, dirname, name, now);
532
533
      #endif

534
      if (! LinuxProcessList_readStatmFile(lp, dirname, name))
535
536
         goto errorReadingProcess;

537
      proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
538
539

      char command[MAX_NAME+1];
540
541
      unsigned long long int lasttimes = (lp->utime + lp->stime);
      if (! LinuxProcessList_readStatFile(proc, dirname, name, command))
542
         goto errorReadingProcess;
543
      if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
544
545
546
547
         LinuxProcess_updateIOPriority(lp);
      float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
      proc->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0);
      if (isnan(proc->percent_cpu)) proc->percent_cpu = 0.0;
Hisham Muhammad's avatar
Hisham Muhammad committed
548
      proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(pl->totalMem) * 100.0;
549

550
      if(!preExisting) {
551

552
         if (! LinuxProcessList_statProcessDir(proc, dirname, name, curTime))
553
554
            goto errorReadingProcess;

555
         proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
556
557

         #ifdef HAVE_OPENVZ
558
         LinuxProcessList_readOpenVZData(this, lp, dirname, name);
559
560
561
         #endif
         
         #ifdef HAVE_VSERVER
562
         if (settings->flags & PROCESS_FLAG_LINUX_VSERVER)
563
            LinuxProcessList_readVServerData(lp, dirname, name);
564
565
         #endif

566
         if (! LinuxProcessList_readCmdlineFile(proc, dirname, name))
567
568
            goto errorReadingProcess;

569
         ProcessList_add(pl, proc);
570
      } else {
Hisham Muhammad's avatar
Hisham Muhammad committed
571
         if (settings->updateProcessNames) {
572
            if (! LinuxProcessList_readCmdlineFile(proc, dirname, name))
573
574
575
576
577
               goto errorReadingProcess;
         }
      }

      #ifdef HAVE_CGROUP
578
      if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
579
         LinuxProcessList_readCGroupFile(lp, dirname, name);
580
581
582
      #endif
      
      #ifdef HAVE_OOM
583
      LinuxProcessList_readOomData(lp, dirname, name);
584
585
      #endif

586
587
588
589
590
591
592
593
594
      if (proc->state == 'Z') {
         free(proc->comm);
         proc->basenameOffset = -1;
         proc->comm = strdup(command);
      } else if (Process_isThread(proc)) {
         if (settings->showThreadNames || Process_isKernelThread(proc) || proc->state == 'Z') {
            free(proc->comm);
            proc->basenameOffset = -1;
            proc->comm = strdup(command);
Hisham Muhammad's avatar
Hisham Muhammad committed
595
         } else if (settings->showThreadNames) {
596
            if (! LinuxProcessList_readCmdlineFile(proc, dirname, name))
597
598
               goto errorReadingProcess;
         }
599
         if (Process_isKernelThread(proc)) {
600
            pl->kernelThreads++;
601
         } else {
602
            pl->userlandThreads++;
603
604
605
         }
      }

606
      pl->totalTasks++;
607
      if (proc->state == 'R')
608
         pl->runningTasks++;
609
      proc->updated = true;
610
611
612
613
      continue;

      // Exception handler.
      errorReadingProcess: {
614
         if (preExisting) {
615
            ProcessList_remove(pl, proc);
616
         } else {
617
            Process_delete((Object*)proc);
618
         }
619
620
621
622
623
624
      }
   }
   closedir(dir);
   return true;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
625
static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
626
627
   unsigned long long int swapFree = 0;

628
629
630
631
   FILE* file = fopen(PROCMEMINFOFILE, "r");
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCMEMINFOFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
   char buffer[128];
   while (fgets(buffer, 128, file)) {

      switch (buffer[0]) {
      case 'M':
         if (String_startsWith(buffer, "MemTotal:"))
            sscanf(buffer, "MemTotal: %32llu kB", &this->totalMem);
         else if (String_startsWith(buffer, "MemFree:"))
            sscanf(buffer, "MemFree: %32llu kB", &this->freeMem);
         else if (String_startsWith(buffer, "MemShared:"))
            sscanf(buffer, "MemShared: %32llu kB", &this->sharedMem);
         break;
      case 'B':
         if (String_startsWith(buffer, "Buffers:"))
            sscanf(buffer, "Buffers: %32llu kB", &this->buffersMem);
         break;
      case 'C':
         if (String_startsWith(buffer, "Cached:"))
            sscanf(buffer, "Cached: %32llu kB", &this->cachedMem);
         break;
      case 'S':
         if (String_startsWith(buffer, "SwapTotal:"))
            sscanf(buffer, "SwapTotal: %32llu kB", &this->totalSwap);
         if (String_startsWith(buffer, "SwapFree:"))
            sscanf(buffer, "SwapFree: %32llu kB", &swapFree);
         break;
658
659
660
661
662
663
      }
   }

   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
664
}
665

Hisham Muhammad's avatar
Hisham Muhammad committed
666
static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
667
668
   unsigned long long int usertime, nicetime, systemtime, idletime;

Hisham Muhammad's avatar
Hisham Muhammad committed
669
   FILE* file = fopen(PROCSTATFILE, "r");
670
671
672
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
673
   int cpus = this->super.cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
674
   assert(cpus > 0);
675
676
677
678
679
680
681
682
683
   for (int i = 0; i <= cpus; i++) {
      char buffer[256];
      int cpuid;
      unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
      unsigned long long int systemalltime, idlealltime, totaltime, virtalltime;
      ioWait = irq = softIrq = steal = guest = guestnice = 0;
      // Dependending on your kernel version,
      // 5, 7, 8 or 9 of these fields will be set.
      // The rest will remain at zero.
684
685
      char* ok = fgets(buffer, 255, file);
      if (!ok) buffer[0] = '\0';
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
      if (i == 0)
         sscanf(buffer, "cpu  %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
      else {
         sscanf(buffer, "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
         assert(cpuid == i - 1);
      }
      // Guest time is already accounted in usertime
      usertime = usertime - guest;
      nicetime = nicetime - guestnice;
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
      idlealltime = idletime + ioWait;
      systemalltime = systemtime + irq + softIrq;
      virtalltime = guest + guestnice;
      totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
      CPUData* cpuData = &(this->cpus[i]);
      assert (usertime >= cpuData->userTime);
      assert (nicetime >= cpuData->niceTime);
      assert (systemtime >= cpuData->systemTime);
      assert (idletime >= cpuData->idleTime);
      assert (totaltime >= cpuData->totalTime);
      assert (systemalltime >= cpuData->systemAllTime);
      assert (idlealltime >= cpuData->idleAllTime);
      assert (ioWait >= cpuData->ioWaitTime);
      assert (irq >= cpuData->irqTime);
      assert (softIrq >= cpuData->softIrqTime);
      assert (steal >= cpuData->stealTime);
      assert (virtalltime >= cpuData->guestTime);
      cpuData->userPeriod = usertime - cpuData->userTime;
      cpuData->nicePeriod = nicetime - cpuData->niceTime;
      cpuData->systemPeriod = systemtime - cpuData->systemTime;
      cpuData->systemAllPeriod = systemalltime - cpuData->systemAllTime;
      cpuData->idleAllPeriod = idlealltime - cpuData->idleAllTime;
      cpuData->idlePeriod = idletime - cpuData->idleTime;
      cpuData->ioWaitPeriod = ioWait - cpuData->ioWaitTime;
      cpuData->irqPeriod = irq - cpuData->irqTime;
      cpuData->softIrqPeriod = softIrq - cpuData->softIrqTime;
      cpuData->stealPeriod = steal - cpuData->stealTime;
      cpuData->guestPeriod = virtalltime - cpuData->guestTime;
      cpuData->totalPeriod = totaltime - cpuData->totalTime;
      cpuData->userTime = usertime;
      cpuData->niceTime = nicetime;
      cpuData->systemTime = systemtime;
      cpuData->systemAllTime = systemalltime;
      cpuData->idleAllTime = idlealltime;
      cpuData->idleTime = idletime;
      cpuData->ioWaitTime = ioWait;
      cpuData->irqTime = irq;
      cpuData->softIrqTime = softIrq;
      cpuData->stealTime = steal;
      cpuData->guestTime = virtalltime;
      cpuData->totalTime = totaltime;
   }
   double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
740
741
742
   return period;
}

743
void ProcessList_goThroughEntries(ProcessList* super) {
Hisham Muhammad's avatar
Hisham Muhammad committed
744
   LinuxProcessList* this = (LinuxProcessList*) super;
Hisham Muhammad's avatar
Hisham Muhammad committed
745

Hisham Muhammad's avatar
Hisham Muhammad committed
746
   LinuxProcessList_scanMemoryInfo(super);
Hisham Muhammad's avatar
Hisham Muhammad committed
747
   double period = LinuxProcessList_scanCPUTime(this);
748
749
750

   struct timeval tv;
   gettimeofday(&tv, NULL);
751
   LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv);
752
}