ProcessList.c 28.7 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
3
4
5
6
7
/*
htop - ProcessList.c
(C) 2004,2005 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

8
9
10
11
12
#ifndef CONFIG_H
#define CONFIG_H
#include "config.h"
#endif

Hisham Muhammad's avatar
Hisham Muhammad committed
13
14
#include "ProcessList.h"
#include "Process.h"
15
#include "Vector.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
16
17
#include "UsersTable.h"
#include "Hashtable.h"
18
#include "String.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
19
20
21
22
23
24
25
26
27
28

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/utsname.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
29
#include <stdarg.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
30
31
32
33
34
35
36
37
38
39

#include "debug.h"
#include <assert.h>

/*{
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif

#ifndef PROCSTATFILE
40
#define PROCSTATFILE PROCDIR "/stat"
Hisham Muhammad's avatar
Hisham Muhammad committed
41
42
43
#endif

#ifndef PROCMEMINFOFILE
44
#define PROCMEMINFOFILE PROCDIR "/meminfo"
Hisham Muhammad's avatar
Hisham Muhammad committed
45
46
47
#endif

#ifndef MAX_NAME
Hisham Muhammad's avatar
Hisham Muhammad committed
48
49
50
51
#define MAX_NAME 128
#endif

#ifndef MAX_READ
52
#define MAX_READ 2048
Hisham Muhammad's avatar
Hisham Muhammad committed
53
54
#endif

55
#ifndef PER_PROCESSOR_FIELDS
56
#define PER_PROCESSOR_FIELDS 22
57
58
#endif

Hisham Muhammad's avatar
Hisham Muhammad committed
59
60
61
62
}*/

/*{

63
#ifdef DEBUG_PROC
64
65
66
typedef int(*vxscanf)(void*, const char*, va_list);
#endif

Hisham Muhammad's avatar
Hisham Muhammad committed
67
typedef struct ProcessList_ {
68
69
   Vector* processes;
   Vector* processes2;
Hisham Muhammad's avatar
Hisham Muhammad committed
70
71
72
73
74
75
76
77
   Hashtable* processTable;
   Process* prototype;
   UsersTable* usersTable;

   int processorCount;
   int totalTasks;
   int runningTasks;

78
   // Must match number of PER_PROCESSOR_FIELDS constant
79
80
81
   unsigned long long int* totalTime;
   unsigned long long int* userTime;
   unsigned long long int* systemTime;
82
   unsigned long long int* systemAllTime;
83
   unsigned long long int* idleAllTime;
84
85
   unsigned long long int* idleTime;
   unsigned long long int* niceTime;
86
87
88
89
   unsigned long long int* ioWaitTime;
   unsigned long long int* irqTime;
   unsigned long long int* softIrqTime;
   unsigned long long int* stealTime;
90
91
92
   unsigned long long int* totalPeriod;
   unsigned long long int* userPeriod;
   unsigned long long int* systemPeriod;
93
   unsigned long long int* systemAllPeriod;
94
   unsigned long long int* idleAllPeriod;
95
96
   unsigned long long int* idlePeriod;
   unsigned long long int* nicePeriod;
97
98
99
100
   unsigned long long int* ioWaitPeriod;
   unsigned long long int* irqPeriod;
   unsigned long long int* softIrqPeriod;
   unsigned long long int* stealPeriod;
101
102
103
104
105
106
107
108
109
110

   unsigned long long int totalMem;
   unsigned long long int usedMem;
   unsigned long long int freeMem;
   unsigned long long int sharedMem;
   unsigned long long int buffersMem;
   unsigned long long int cachedMem;
   unsigned long long int totalSwap;
   unsigned long long int usedSwap;
   unsigned long long int freeSwap;
Hisham Muhammad's avatar
Hisham Muhammad committed
111
112
113
114
115
116
117
118
119
120
121

   ProcessField* fields;
   ProcessField sortKey;
   int direction;
   bool hideThreads;
   bool shadowOtherUsers;
   bool hideKernelThreads;
   bool hideUserlandThreads;
   bool treeView;
   bool highlightBaseName;
   bool highlightMegabytes;
122
   bool highlightThreads;
123
   bool detailedCPUTime;
124
   #ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
125
126
   FILE* traceFile;
   #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
127
128
129
130

} ProcessList;
}*/

131
static ProcessField defaultHeaders[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
Hisham Muhammad's avatar
Hisham Muhammad committed
132

133
#ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
134
135
136
137

#define ProcessList_read(this, buffer, format, ...) ProcessList_xread(this, (vxscanf) vsscanf, buffer, format, ## __VA_ARGS__ )
#define ProcessList_fread(this, file, format, ...)  ProcessList_xread(this, (vxscanf) vfscanf, file, format, ## __VA_ARGS__ )

138
static FILE* ProcessList_fopen(ProcessList* this, const char* path, const char* mode) {
Hisham Muhammad's avatar
Hisham Muhammad committed
139
140
141
142
143
144
145
146
147
148
149
150
   fprintf(this->traceFile, "[%s]\n", path);
   return fopen(path, mode);
}

static inline int ProcessList_xread(ProcessList* this, vxscanf fn, void* buffer, char* format, ...) {
   va_list ap;
   va_start(ap, format);
   int num = fn(buffer, format, ap);
   va_end(format);
   va_start(ap, format);
   while (*format) {
      char ch = *format;
151
152
153
154
      char* c; int* d;
      long int* ld; unsigned long int* lu;
      long long int* lld; unsigned long long int* llu;
      char** s;
Hisham Muhammad's avatar
Hisham Muhammad committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
      if (ch != '%') {
         fprintf(this->traceFile, "%c", ch);
         format++;
         continue;
      }
      format++;
      switch(*format) {
      case 'c': c = va_arg(ap, char*);  fprintf(this->traceFile, "%c", *c); break;
      case 'd': d = va_arg(ap, int*);   fprintf(this->traceFile, "%d", *d); break;
      case 's': s = va_arg(ap, char**); fprintf(this->traceFile, "%s", *s); break;
      case 'l':
         format++;
         switch (*format) {
         case 'd': ld = va_arg(ap, long int*); fprintf(this->traceFile, "%ld", *ld); break;
         case 'u': lu = va_arg(ap, unsigned long int*); fprintf(this->traceFile, "%lu", *lu); break;
170
171
172
173
174
175
         case 'l':
            format++;
            switch (*format) {
            case 'd': lld = va_arg(ap, long long int*); fprintf(this->traceFile, "%lld", *lld); break;
            case 'u': llu = va_arg(ap, unsigned long long int*); fprintf(this->traceFile, "%llu", *llu); break;
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
         }
      }
      format++;
   }
   fprintf(this->traceFile, "\n");
   va_end(format);
   return num;
}

#else

#ifndef ProcessList_read
#define ProcessList_fopen(this, path, mode) fopen(path, mode)
#define ProcessList_read(this, buffer, format, ...) sscanf(buffer, format, ## __VA_ARGS__ )
#define ProcessList_fread(this, file, format, ...) fscanf(file, format, ## __VA_ARGS__ )
#endif

#endif

195
196
197
198
199
200
201
202
203
204
static inline void ProcessList_allocatePerProcessorBuffers(ProcessList* this, int procs) {
   unsigned long long int** bufferPtr = &(this->totalTime);
   unsigned long long int* buffer = calloc(procs * PER_PROCESSOR_FIELDS, sizeof(unsigned long long int));
   for (int i = 0; i < PER_PROCESSOR_FIELDS; i++) {
      *bufferPtr = buffer;
      bufferPtr++;
      buffer += procs;
   }
}

Hisham Muhammad's avatar
Hisham Muhammad committed
205
206
207
ProcessList* ProcessList_new(UsersTable* usersTable) {
   ProcessList* this;
   this = malloc(sizeof(ProcessList));
208
   this->processes = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
209
   this->processTable = Hashtable_new(70, false);
210
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
211
212
213
214
   this->prototype = Process_new(this);
   this->usersTable = usersTable;
   
   /* tree-view auxiliary buffers */
215
   this->processes2 = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
216
   
217
   #ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
218
219
   this->traceFile = fopen("/tmp/htop-proc-trace", "w");
   #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
220
221
222
223
224
225
226
227
228
229
230

   FILE* status = fopen(PROCSTATFILE, "r");
   assert(status != NULL);
   char buffer[256];
   int procs = -1;
   do {
      procs++;
      fgets(buffer, 255, status);
   } while (String_startsWith(buffer, "cpu"));
   fclose(status);
   this->processorCount = procs - 1;
231
232
233
   
   ProcessList_allocatePerProcessorBuffers(this, procs);

Hisham Muhammad's avatar
Hisham Muhammad committed
234
235
236
237
238
239
   for (int i = 0; i < procs; i++) {
      this->totalTime[i] = 1;
      this->totalPeriod[i] = 1;
   }

   this->fields = calloc(sizeof(ProcessField), LAST_PROCESSFIELD+1);
240
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
241
242
243
244
245
246
247
248
249
250
251
252
253
   // (and ProcessFields into proper objects).
   for (int i = 0; defaultHeaders[i]; i++) {
      this->fields[i] = defaultHeaders[i];
   }
   this->sortKey = PERCENT_CPU;
   this->direction = 1;
   this->hideThreads = false;
   this->shadowOtherUsers = false;
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
254
   this->detailedCPUTime = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
255
256
257
258
259
260

   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
261
262
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
Hisham Muhammad's avatar
Hisham Muhammad committed
263
264
   Process_delete((Object*)this->prototype);

265
266
   // Free first entry only;
   // other fields are offsets of the same buffer
Hisham Muhammad's avatar
Hisham Muhammad committed
267
268
   free(this->totalTime);

269
   #ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
270
271
272
   fclose(this->traceFile);
   #endif

Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
276
277
278
279
280
281
282
283
284
   free(this->fields);
   free(this);
}

void ProcessList_invertSortOrder(ProcessList* this) {
   if (this->direction == 1)
      this->direction = -1;
   else
      this->direction = 1;
}

RichString ProcessList_printHeader(ProcessList* this) {
285
   RichString out;
286
   RichString_initVal(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
287
288
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
289
      char* field = Process_fieldTitles[fields[i]];
Hisham Muhammad's avatar
Hisham Muhammad committed
290
291
292
293
294
295
296
297
      if (this->sortKey == fields[i])
         RichString_append(&out, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
      else
         RichString_append(&out, CRT_colors[PANEL_HEADER_FOCUS], field);
   }
   return out;
}

298
static void ProcessList_add(ProcessList* this, Process* p) {
299
300
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
   assert(Hashtable_get(this->processTable, p->pid) == NULL);
301
   Vector_add(this->processes, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
302
   Hashtable_put(this->processTable, p->pid, p);
303
304
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
305
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
306
307
}

308
static void ProcessList_remove(ProcessList* this, Process* p) {
309
310
311
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
   Process* pp = Hashtable_remove(this->processTable, p->pid);
312
   assert(pp == p); (void)pp;
313
   unsigned int pid = p->pid;
314
   int index = Vector_indexOf(this->processes, p, Process_pidCompare);
315
   assert(index != -1);
Hisham Muhammad's avatar
Hisham Muhammad committed
316
   if (index >= 0) Vector_remove(this->processes, index);
317
   assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
318
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
319
320
321
}

Process* ProcessList_get(ProcessList* this, int index) {
322
   return (Process*) (Vector_get(this->processes, index));
Hisham Muhammad's avatar
Hisham Muhammad committed
323
324
325
}

int ProcessList_size(ProcessList* this) {
326
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
327
328
}

329
static void ProcessList_buildTree(ProcessList* this, int pid, int level, int indent, int direction) {
330
   Vector* children = Vector_new(PROCESS_CLASS, false, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
331

332
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
333
      Process* process = (Process*) (Vector_get(this->processes, i));
334
      if (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid)) {
335
336
         Process* process = (Process*) (Vector_take(this->processes, i));
         Vector_add(children, process);
Hisham Muhammad's avatar
Hisham Muhammad committed
337
338
      }
   }
339
   int size = Vector_size(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
340
   for (int i = 0; i < size; i++) {
341
      Process* process = (Process*) (Vector_get(children, i));
342
      int s = this->processes2->items;
Hisham Muhammad's avatar
Hisham Muhammad committed
343
      if (direction == 1)
344
         Vector_add(this->processes2, process);
Hisham Muhammad's avatar
Hisham Muhammad committed
345
      else
346
         Vector_insert(this->processes2, 0, process);
347
      assert(this->processes2->items == s+1); (void)s;
Hisham Muhammad's avatar
Hisham Muhammad committed
348
349
350
351
352
353
      int nextIndent = indent;
      if (i < size - 1)
         nextIndent = indent | (1 << level);
      ProcessList_buildTree(this, process->pid, level+1, nextIndent, direction);
      process->indent = indent | (1 << level);
   }
354
   Vector_delete(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
355
356
357
358
}

void ProcessList_sort(ProcessList* this) {
   if (!this->treeView) {
359
      Vector_sort(this->processes);
Hisham Muhammad's avatar
Hisham Muhammad committed
360
   } else {
361
      // Save settings
Hisham Muhammad's avatar
Hisham Muhammad committed
362
363
      int direction = this->direction;
      int sortKey = this->sortKey;
364
      // Sort by PID
Hisham Muhammad's avatar
Hisham Muhammad committed
365
366
      this->sortKey = PID;
      this->direction = 1;
367
      Vector_sort(this->processes);
368
      // Restore settings
Hisham Muhammad's avatar
Hisham Muhammad committed
369
370
      this->sortKey = sortKey;
      this->direction = direction;
371
      // Take PID 1 as root and add to the new listing
372
      int vsize = Vector_size(this->processes);
373
      Process* init = (Process*) (Vector_take(this->processes, 0));
374
375
376
      // This assertion crashes on hardened kernels.
      // I wonder how well tree view works on those systems.
      // assert(init->pid == 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
377
      init->indent = 0;
378
      Vector_add(this->processes2, init);
379
      // Recursively empty list
Hisham Muhammad's avatar
Hisham Muhammad committed
380
      ProcessList_buildTree(this, init->pid, 0, 0, direction);
381
      // Add leftovers
382
383
384
385
      while (Vector_size(this->processes)) {
         Process* p = (Process*) (Vector_take(this->processes, 0));
         p->indent = 0;
         Vector_add(this->processes2, p);
386
         ProcessList_buildTree(this, p->pid, 0, 0, direction);
387
388
389
      }
      assert(Vector_size(this->processes2) == vsize); (void)vsize;
      assert(Vector_size(this->processes) == 0);
390
      // Swap listings around
391
      Vector* t = this->processes;
Hisham Muhammad's avatar
Hisham Muhammad committed
392
393
394
395
396
      this->processes = this->processes2;
      this->processes2 = t;
   }
}

397
static int ProcessList_readStatFile(ProcessList* this, Process *proc, FILE *f, char *command) {
Hisham Muhammad's avatar
Hisham Muhammad committed
398
   static char buf[MAX_READ];
399
   unsigned long int zero;
Hisham Muhammad's avatar
Hisham Muhammad committed
400
401
402
403

   int size = fread(buf, 1, MAX_READ, f);
   if(!size) return 0;

404
   assert(proc->pid == atoi(buf));
Hisham Muhammad's avatar
Hisham Muhammad committed
405
406
407
408
409
410
411
412
413
414
415
416
   char *location = strchr(buf, ' ');
   if(!location) return 0;

   location += 2;
   char *end = strrchr(location, ')');
   if(!end) return 0;
   
   int commsize = end - location;
   memcpy(command, location, commsize);
   command[commsize] = '\0';
   location = end + 2;
   
417
   #ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
418
   int num = ProcessList_read(this, location, 
419
      "%c %u %u %u %u %d %lu %lu %lu %lu "
Hisham Muhammad's avatar
Hisham Muhammad committed
420
421
422
423
424
      "%lu %lu %lu %ld %ld %ld %ld %ld %ld "
      "%lu %lu %ld %lu %lu %lu %lu %lu "
      "%lu %lu %lu %lu %lu %lu %lu %lu "
      "%d %d",
      &proc->state, &proc->ppid, &proc->pgrp, &proc->session, &proc->tty_nr, 
425
426
427
      &proc->tpgid, &proc->flags,
      &proc->minflt, &proc->cminflt, &proc->majflt, &proc->cmajflt,
      &proc->utime, &proc->stime, &proc->cutime, &proc->cstime, 
428
      &proc->priority, &proc->nice, &proc->nlwp, &proc->itrealvalue,
Hisham Muhammad's avatar
Hisham Muhammad committed
429
430
431
432
433
      &proc->starttime, &proc->vsize, &proc->rss, &proc->rlim, 
      &proc->startcode, &proc->endcode, &proc->startstack, &proc->kstkesp, 
      &proc->kstkeip, &proc->signal, &proc->blocked, &proc->sigignore, 
      &proc->sigcatch, &proc->wchan, &proc->nswap, &proc->cnswap, 
      &proc->exit_signal, &proc->processor);
434
435
436
   #else
   long int uzero;
   int num = ProcessList_read(this, location, 
437
      "%c %u %u %u %u %d %lu %lu %lu %lu "
438
439
440
441
442
443
444
445
      "%lu %lu %lu %ld %ld %ld %ld %ld %ld "
      "%lu %lu %ld %lu %lu %lu %lu %lu "
      "%lu %lu %lu %lu %lu %lu %lu %lu "
      "%d %d",
      &proc->state, &proc->ppid, &proc->pgrp, &proc->session, &proc->tty_nr, 
      &proc->tpgid, &proc->flags,
      &zero, &zero, &zero, &zero,
      &proc->utime, &proc->stime, &proc->cutime, &proc->cstime, 
446
      &proc->priority, &proc->nice, &proc->nlwp, &uzero,
447
448
449
450
451
452
      &zero, &zero, &uzero, &zero, 
      &zero, &zero, &zero, &zero, 
      &zero, &zero, &zero, &zero, 
      &zero, &zero, &zero, &zero, 
      &proc->exit_signal, &proc->processor);
   #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
453
454
455
456
457
458
459
460
461
   
   // This assert is always valid on 2.4, but reportedly not always valid on 2.6.
   // TODO: Check if the semantics of this field has changed.
   // assert(zero == 0);
   
   if(num != 37) return 0;
   return 1;
}

462
static bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* name) {
Hisham Muhammad's avatar
Hisham Muhammad committed
463
464
   char statusfilename[MAX_NAME+1];
   statusfilename[MAX_NAME] = '\0';
465
466
467
468
469
470
471
472

   snprintf(statusfilename, MAX_NAME, "%s/%s", dirname, name);
   struct stat sstat;
   int statok = stat(statusfilename, &sstat);
   if (statok == -1)
      return false;
   proc->st_uid = sstat.st_uid;
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
473
474
}

475
#ifdef HAVE_TASKSTATS
476
477

static void ProcessList_readIoFile(ProcessList* this, Process* proc, char* dirname, char* name) {
478
479
480
481
482
483
   char iofilename[MAX_NAME+1];
   iofilename[MAX_NAME] = '\0';

   snprintf(iofilename, MAX_NAME, "%s/%s/io", dirname, name);
   FILE* io = ProcessList_fopen(this, iofilename, "r");
   if (io) {
484
485
      char buffer[256];
      buffer[255] = '\0';
486
487
488
489
490
      struct timeval tv;
      gettimeofday(&tv,NULL);
      unsigned long long now = tv.tv_sec*1000+tv.tv_usec/1000;
      unsigned long long last_read = proc->io_read_bytes;
      unsigned long long last_write = proc->io_write_bytes;
Hisham Muhammad's avatar
Hisham Muhammad committed
491
      while (fgets(buffer, 255, io)) {
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
         if (ProcessList_read(this, buffer, "rchar: %llu", &proc->io_rchar)) continue;
         if (ProcessList_read(this, buffer, "wchar: %llu", &proc->io_wchar)) continue;
         if (ProcessList_read(this, buffer, "syscr: %llu", &proc->io_syscr)) continue;
         if (ProcessList_read(this, buffer, "syscw: %llu", &proc->io_syscw)) continue;
         if (ProcessList_read(this, buffer, "read_bytes: %llu", &proc->io_read_bytes)) {
            proc->io_rate_read_bps = 
               ((double)(proc->io_read_bytes - last_read))/(((double)(now - proc->io_rate_read_time))/1000);
            proc->io_rate_read_time = now;
            continue;
         }
         if (ProcessList_read(this, buffer, "write_bytes: %llu", &proc->io_write_bytes)) {
            proc->io_rate_write_bps = 
               ((double)(proc->io_write_bytes - last_write))/(((double)(now - proc->io_rate_write_time))/1000);
            proc->io_rate_write_time = now;
            continue;
         }
         ProcessList_read(this, buffer, "cancelled_write_bytes: %llu", &proc->io_cancelled_write_bytes);
      }
      fclose(io);
   }
}
513

514
515
#endif

516
static bool ProcessList_processEntries(ProcessList* this, char* dirname, Process* parent, float period) {
Hisham Muhammad's avatar
Hisham Muhammad committed
517
518
519
   DIR* dir;
   struct dirent* entry;
   Process* prototype = this->prototype;
Hisham Muhammad's avatar
Hisham Muhammad committed
520
   int parentPid = parent ? parent->pid : 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
521
522

   dir = opendir(dirname);
523
524
525
   if (!dir) return false;
   int processors = this->processorCount;
   bool showUserlandThreads = !this->hideUserlandThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
   while ((entry = readdir(dir)) != NULL) {
      char* name = entry->d_name;
      int pid;
      // filename is a number: process directory
      pid = atoi(name);

      // The RedHat kernel hides threads with a dot.
      // I believe this is non-standard.
      bool isThread = false;
      if ((!this->hideThreads) && pid == 0 && name[0] == '.') {
         char* tname = name + 1;
         pid = atoi(tname);
         if (pid > 0)
            isThread = true;
      }

542
      if (pid > 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
543

Hisham Muhammad's avatar
Hisham Muhammad committed
544
545
546
547
         FILE* status;
         char statusfilename[MAX_NAME+1];
         char command[PROCESS_COMM_LEN + 1];

548
         Process* process = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
549
         Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);
550

551
         if (existingProcess) {
552
            assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1);
553
            process = existingProcess;
554
            assert(process->pid == pid);
555
         } else {
556
557
558
559
560
561
562
            if (parent && parent->pid == pid) {
               process = parent;
            } else {
               process = prototype;
               assert(process->comm == NULL);
               process->pid = pid;
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
563
         }
564
         process->tgid = parent ? parent->pid : pid;
565
566
567
568
569
570
571
572
573

         if (showUserlandThreads && (!parent || pid != parent->pid)) {
            char subdirname[MAX_NAME+1];
            snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
   
            if (ProcessList_processEntries(this, subdirname, process, period))
               continue;
         }

574
575
576
577
         #ifdef HAVE_TASKSTATS        
         ProcessList_readIoFile(this, process, dirname, name);
         #endif

Hisham Muhammad's avatar
Hisham Muhammad committed
578
579
         process->updated = true;

580
581
582
583
         if (!existingProcess)
            if (! ProcessList_readStatusFile(this, process, dirname, name))
               goto errorReadingProcess;

584
585
586
587
588
589
590
591
         snprintf(statusfilename, MAX_NAME, "%s/%s/statm", dirname, name);
         status = ProcessList_fopen(this, statusfilename, "r");

         if(!status) {
            goto errorReadingProcess;
         }
         int num = ProcessList_fread(this, status, "%d %d %d %d %d %d %d",
             &process->m_size, &process->m_resident, &process->m_share, 
592
             &process->m_trs, &process->m_lrs, &process->m_drs, 
593
594
595
596
597
598
599
600
601
             &process->m_dt);

         fclose(status);
         if(num != 7)
            goto errorReadingProcess;

         if (this->hideKernelThreads && process->m_size == 0)
            goto errorReadingProcess;

602
603
604
605
606
         int lasttimes = (process->utime + process->stime);

         snprintf(statusfilename, MAX_NAME, "%s/%s/stat", dirname, name);
         
         status = ProcessList_fopen(this, statusfilename, "r");
607
         if (status == NULL)
608
609
610
611
            goto errorReadingProcess;

         int success = ProcessList_readStatFile(this, process, status, command);
         fclose(status);
612
         if(!success) {
613
            goto errorReadingProcess;
614
         }
615

Hisham Muhammad's avatar
Hisham Muhammad committed
616
         if(!existingProcess) {
617
            process->user = UsersTable_getRef(this->usersTable, process->st_uid);
618
619
620
621

            #ifdef HAVE_OPENVZ
            if (access("/proc/vz", R_OK) != 0) {
               process->vpid = process->pid;
622
               process->ctid = 0;
623
624
625
626
627
628
629
630
631
632
633
634
            } else {
               snprintf(statusfilename, MAX_NAME, "%s/%s/stat", dirname, name);
               status = ProcessList_fopen(this, statusfilename, "r");
               if (status == NULL) 
                  goto errorReadingProcess;
               num = ProcessList_fread(this, status, 
                  "%*u %*s %*c %*u %*u %*u %*u %*u %*u %*u "
                  "%*u %*u %*u %*u %*u %*u %*u %*u "
                  "%*u %*u %*u %*u %*u %*u %*u %*u "
                  "%*u %*u %*u %*u %*u %*u %*u %*u "
                  "%*u %*u %*u %*u %*u %*u %*u %*u "
                  "%*u %*u %*u %*u %*u %*u %*u "
635
                  "%*u %*u %u %u",
636
                  &process->vpid, &process->ctid);
637
638
639
               fclose(status);
            }
            #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
640
641
642
643
644
645
646
647
648

            #ifdef HAVE_VSERVER
            snprintf(statusfilename, MAX_NAME, "%s/%s/status", dirname, name);
            status = ProcessList_fopen(this, statusfilename, "r");
            if (status == NULL) 
               goto errorReadingProcess;
            else {
               char buffer[256];
               process->vxid = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
649
               while (fgets(buffer, 255, status)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670

                  if (String_startsWith(buffer, "VxID:")) {
                     int vxid;
                     int ok = ProcessList_read(this, buffer, "VxID:\t%d", &vxid);
                     if (ok >= 1) {
                        process->vxid = vxid;
                     }
                  }
                  #if defined HAVE_ANCIENT_VSERVER
                  else if (String_startsWith(buffer, "s_context:")) {
                     int vxid;
                     int ok = ProcessList_read(this, buffer, "s_context:\t%d", &vxid);
                     if (ok >= 1) {
                        process->vxid = vxid;
                     }
                  }
                  #endif
               }
               fclose(status);
            }
            #endif
671
 
Hisham Muhammad's avatar
Hisham Muhammad committed
672
            snprintf(statusfilename, MAX_NAME, "%s/%s/cmdline", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
673
            status = ProcessList_fopen(this, statusfilename, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
674
675
676
677
678
679
680
681
682
683
684
            if (!status) {
               goto errorReadingProcess;
            }

            int amtRead = fread(command, 1, PROCESS_COMM_LEN - 1, status);
            if (amtRead > 0) {
               for (int i = 0; i < amtRead; i++)
                  if (command[i] == '\0' || command[i] == '\n')
                     command[i] = ' ';
               command[amtRead] = '\0';
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
685
686
            fclose(status);

Hisham Muhammad's avatar
Hisham Muhammad committed
687
688
689
690
            command[PROCESS_COMM_LEN] = '\0';
            process->comm = String_copy(command);
         }

691
         int percent_cpu = (process->utime + process->stime - lasttimes) / 
692
            period * 100.0;
693
         process->percent_cpu = MAX(MIN(percent_cpu, processors*100.0), 0.0);
694

Hisham Muhammad's avatar
Hisham Muhammad committed
695
         process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / 
696
            (float)(this->totalMem) * 
Hisham Muhammad's avatar
Hisham Muhammad committed
697
698
699
            100.0;

         this->totalTasks++;
Hisham Muhammad's avatar
Hisham Muhammad committed
700
701
702
         if (process->state == 'R') {
            this->runningTasks++;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
703

704
         if (!existingProcess) {
705
706
            process = Process_clone(process);
            ProcessList_add(this, process);
707
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
708
709
710
711
712

         continue;

         // Exception handler.
         errorReadingProcess: {
713
714
715
716
            if (process->comm) {
               free(process->comm);
               process->comm = NULL;
            }
717
718
            if (existingProcess)
               ProcessList_remove(this, process);
719
            assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
720
721
722
723
         }
      }
   }
   closedir(dir);
724
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
725
726
727
}

void ProcessList_scan(ProcessList* this) {
728
   unsigned long long int usertime, nicetime, systemtime, systemalltime, idlealltime, idletime, totaltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
729
   unsigned long long int swapFree = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
730
731
732

   FILE* status;
   char buffer[128];
Hisham Muhammad's avatar
Hisham Muhammad committed
733
   status = ProcessList_fopen(this, PROCMEMINFOFILE, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
734
   assert(status != NULL);
735
   int processors = this->processorCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
736
   while (fgets(buffer, 128, status)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
737
738
739
740

      switch (buffer[0]) {
      case 'M':
         if (String_startsWith(buffer, "MemTotal:"))
741
            ProcessList_read(this, buffer, "MemTotal: %llu kB", &this->totalMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
742
         else if (String_startsWith(buffer, "MemFree:"))
743
            ProcessList_read(this, buffer, "MemFree: %llu kB", &this->freeMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
744
         else if (String_startsWith(buffer, "MemShared:"))
745
            ProcessList_read(this, buffer, "MemShared: %llu kB", &this->sharedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
746
747
748
         break;
      case 'B':
         if (String_startsWith(buffer, "Buffers:"))
749
            ProcessList_read(this, buffer, "Buffers: %llu kB", &this->buffersMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
750
751
752
         break;
      case 'C':
         if (String_startsWith(buffer, "Cached:"))
753
            ProcessList_read(this, buffer, "Cached: %llu kB", &this->cachedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
754
755
756
         break;
      case 'S':
         if (String_startsWith(buffer, "SwapTotal:"))
757
            ProcessList_read(this, buffer, "SwapTotal: %llu kB", &this->totalSwap);
Hisham Muhammad's avatar
Hisham Muhammad committed
758
         if (String_startsWith(buffer, "SwapFree:"))
759
            ProcessList_read(this, buffer, "SwapFree: %llu kB", &swapFree);
Hisham Muhammad's avatar
Hisham Muhammad committed
760
761
762
         break;
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
763

Hisham Muhammad's avatar
Hisham Muhammad committed
764
765
766
767
   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
   fclose(status);

Hisham Muhammad's avatar
Hisham Muhammad committed
768
769
   status = ProcessList_fopen(this, PROCSTATFILE, "r");

Hisham Muhammad's avatar
Hisham Muhammad committed
770
   assert(status != NULL);
771
   for (int i = 0; i <= processors; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
772
773
      char buffer[256];
      int cpuid;
774
      unsigned long long int ioWait, irq, softIrq, steal;
Hisham Muhammad's avatar
Hisham Muhammad committed
775
776
777
778
779
780
      ioWait = irq = softIrq = steal = 0;
      // Dependending on your kernel version,
      // 5, 7 or 8 of these fields will be set.
      // The rest will remain at zero.
      fgets(buffer, 255, status);
      if (i == 0)
781
         ProcessList_read(this, buffer, "cpu  %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal);
Hisham Muhammad's avatar
Hisham Muhammad committed
782
      else {
783
         ProcessList_read(this, buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal);
Hisham Muhammad's avatar
Hisham Muhammad committed
784
785
786
787
         assert(cpuid == i - 1);
      }
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
788
789
790
      idlealltime = idletime + ioWait;
      systemalltime = systemtime + irq + softIrq + steal;
      totaltime = usertime + nicetime + systemalltime + idlealltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
791
792
793
794
795
      assert (usertime >= this->userTime[i]);
      assert (nicetime >= this->niceTime[i]);
      assert (systemtime >= this->systemTime[i]);
      assert (idletime >= this->idleTime[i]);
      assert (totaltime >= this->totalTime[i]);
796
      assert (systemalltime >= this->systemAllTime[i]);
797
      assert (idlealltime >= this->idleAllTime[i]);
798
      assert (ioWait >= this->ioWaitTime[i]);
799
800
801
      assert (irq >= this->irqTime[i]);
      assert (softIrq >= this->softIrqTime[i]);
      assert (steal >= this->stealTime[i]);
Hisham Muhammad's avatar
Hisham Muhammad committed
802
803
804
      this->userPeriod[i] = usertime - this->userTime[i];
      this->nicePeriod[i] = nicetime - this->niceTime[i];
      this->systemPeriod[i] = systemtime - this->systemTime[i];
805
      this->systemAllPeriod[i] = systemalltime - this->systemAllTime[i];
806
      this->idleAllPeriod[i] = idlealltime - this->idleAllTime[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
807
      this->idlePeriod[i] = idletime - this->idleTime[i];
808
809
810
811
      this->ioWaitPeriod[i] = ioWait - this->ioWaitTime[i];
      this->irqPeriod[i] = irq - this->irqTime[i];
      this->softIrqPeriod[i] = softIrq - this->softIrqTime[i];
      this->stealPeriod[i] = steal - this->stealTime[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
812
813
814
815
      this->totalPeriod[i] = totaltime - this->totalTime[i];
      this->userTime[i] = usertime;
      this->niceTime[i] = nicetime;
      this->systemTime[i] = systemtime;
816
      this->systemAllTime[i] = systemalltime;
817
      this->idleAllTime[i] = idlealltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
818
      this->idleTime[i] = idletime;
819
820
821
822
      this->ioWaitTime[i] = ioWait;
      this->irqTime[i] = irq;
      this->softIrqTime[i] = softIrq;
      this->stealTime[i] = steal;
Hisham Muhammad's avatar
Hisham Muhammad committed
823
824
      this->totalTime[i] = totaltime;
   }
825
   float period = (float)this->totalPeriod[0] / processors;
Hisham Muhammad's avatar
Hisham Muhammad committed
826
827
828
   fclose(status);

   // mark all process as "dirty"
829
830
   for (int i = 0; i < Vector_size(this->processes); i++) {
      Process* p = (Process*) Vector_get(this->processes, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
831
832
833
834
835
      p->updated = false;
   }
   
   this->totalTasks = 0;
   this->runningTasks = 0;
836
837

   ProcessList_processEntries(this, PROCDIR, NULL, period);
Hisham Muhammad's avatar
Hisham Muhammad committed
838
   
839
840
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
      Process* p = (Process*) Vector_get(this->processes, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
841
842
843
844
845
846
847
      if (p->updated == false)
         ProcessList_remove(this, p);
      else
         p->updated = false;
   }

}
848
849
850
851
852
853
854
855
856
857
858
859
860
861

ProcessField ProcessList_keyAt(ProcessList* this, int at) {
   int x = 0;
   ProcessField* fields = this->fields;
   ProcessField field;
   for (int i = 0; (field = fields[i]); i++) {
      int len = strlen(Process_fieldTitles[field]);
      if (at >= x && at <= x + len) {
         return field;
      }
      x += len;
   }
   return COMM;
}