ProcessList.c 29.1 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

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

} ProcessList;
}*/

133
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
134

135
#ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
136
137
138
139

#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__ )

140
static FILE* ProcessList_fopen(ProcessList* this, const char* path, const char* mode) {
Hisham Muhammad's avatar
Hisham Muhammad committed
141
142
143
144
145
146
147
148
149
150
151
152
   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;
153
154
155
156
      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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
      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;
172
173
174
175
176
177
         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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
         }
      }
      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

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

   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;
233
234
235
   
   ProcessList_allocatePerProcessorBuffers(this, procs);

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

   this->fields = calloc(sizeof(ProcessField), LAST_PROCESSFIELD+1);
242
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
243
244
245
246
247
248
249
250
   // (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;
Hisham Muhammad's avatar
Hisham Muhammad committed
251
252
   this->showThreadNames = true;
   this->showingThreadNames = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
253
254
255
256
257
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
258
   this->detailedCPUTime = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
259
260
261
262
263
264

   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
265
266
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
Hisham Muhammad's avatar
Hisham Muhammad committed
267
268
   Process_delete((Object*)this->prototype);

269
270
   // Free first entry only;
   // other fields are offsets of the same buffer
Hisham Muhammad's avatar
Hisham Muhammad committed
271
272
   free(this->totalTime);

273
   #ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
274
275
276
   fclose(this->traceFile);
   #endif

Hisham Muhammad's avatar
Hisham Muhammad committed
277
278
279
280
281
282
283
284
285
286
287
288
   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) {
289
   RichString out;
290
   RichString_initVal(out);
Hisham Muhammad's avatar
Hisham Muhammad committed
291
292
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
293
      const char* field = Process_fieldTitles[fields[i]];
Hisham Muhammad's avatar
Hisham Muhammad committed
294
295
296
297
298
299
300
301
      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;
}

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

312
static void ProcessList_remove(ProcessList* this, Process* p) {
313
314
315
   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);
316
   assert(pp == p); (void)pp;
317
   unsigned int pid = p->pid;
Hisham Muhammad's avatar
Hisham Muhammad committed
318
319
320
   int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
   assert(idx != -1);
   if (idx >= 0) Vector_remove(this->processes, idx);
321
   assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
322
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
323
324
}

Hisham Muhammad's avatar
Hisham Muhammad committed
325
326
Process* ProcessList_get(ProcessList* this, int idx) {
   return (Process*) (Vector_get(this->processes, idx));
Hisham Muhammad's avatar
Hisham Muhammad committed
327
328
329
}

int ProcessList_size(ProcessList* this) {
330
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
331
332
}

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

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

void ProcessList_sort(ProcessList* this) {
   if (!this->treeView) {
363
      Vector_sort(this->processes);
Hisham Muhammad's avatar
Hisham Muhammad committed
364
   } else {
365
      // Save settings
Hisham Muhammad's avatar
Hisham Muhammad committed
366
367
      int direction = this->direction;
      int sortKey = this->sortKey;
368
      // Sort by PID
Hisham Muhammad's avatar
Hisham Muhammad committed
369
370
      this->sortKey = PID;
      this->direction = 1;
371
      Vector_sort(this->processes);
372
      // Restore settings
Hisham Muhammad's avatar
Hisham Muhammad committed
373
374
      this->sortKey = sortKey;
      this->direction = direction;
375
      // Take PID 1 as root and add to the new listing
376
      int vsize = Vector_size(this->processes);
377
      Process* init = (Process*) (Vector_take(this->processes, 0));
378
379
380
      // 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
381
      init->indent = 0;
382
      Vector_add(this->processes2, init);
383
      // Recursively empty list
Hisham Muhammad's avatar
Hisham Muhammad committed
384
      ProcessList_buildTree(this, init->pid, 0, 0, direction);
385
      // Add leftovers
386
387
388
389
      while (Vector_size(this->processes)) {
         Process* p = (Process*) (Vector_take(this->processes, 0));
         p->indent = 0;
         Vector_add(this->processes2, p);
390
         ProcessList_buildTree(this, p->pid, 0, 0, direction);
391
392
393
      }
      assert(Vector_size(this->processes2) == vsize); (void)vsize;
      assert(Vector_size(this->processes) == 0);
394
      // Swap listings around
395
      Vector* t = this->processes;
Hisham Muhammad's avatar
Hisham Muhammad committed
396
397
398
399
400
      this->processes = this->processes2;
      this->processes2 = t;
   }
}

Hisham Muhammad's avatar
Hisham Muhammad committed
401
static int ProcessList_readStatFile(Process *proc, FILE *f, char *command) {
Hisham Muhammad's avatar
Hisham Muhammad committed
402
   static char buf[MAX_READ];
403
   unsigned long int zero;
Hisham Muhammad's avatar
Hisham Muhammad committed
404
405
406
407

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

408
   assert(proc->pid == atoi(buf));
Hisham Muhammad's avatar
Hisham Muhammad committed
409
410
411
412
413
414
415
416
417
418
419
420
   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;
   
421
   #ifdef DEBUG_PROC
Hisham Muhammad's avatar
Hisham Muhammad committed
422
   int num = ProcessList_read(this, location, 
423
      "%c %u %u %u %u %d %lu %lu %lu %lu "
Hisham Muhammad's avatar
Hisham Muhammad committed
424
425
426
427
428
      "%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, 
429
430
431
      &proc->tpgid, &proc->flags,
      &proc->minflt, &proc->cminflt, &proc->majflt, &proc->cmajflt,
      &proc->utime, &proc->stime, &proc->cutime, &proc->cstime, 
432
      &proc->priority, &proc->nice, &proc->nlwp, &proc->itrealvalue,
Hisham Muhammad's avatar
Hisham Muhammad committed
433
434
435
436
437
      &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);
438
439
440
   #else
   long int uzero;
   int num = ProcessList_read(this, location, 
Hisham Muhammad's avatar
Hisham Muhammad committed
441
      "%c %d %u %u %u %d %lu %lu %lu %lu "
442
443
444
445
446
447
448
449
      "%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, 
450
      &proc->priority, &proc->nice, &proc->nlwp, &uzero,
451
452
453
454
455
456
      &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
457
458
459
460
461
462
463
464
465
   
   // 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;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
466
static bool ProcessList_readStatusFile(Process* proc, const char* dirname, char* name) {
Hisham Muhammad's avatar
Hisham Muhammad committed
467
468
   char statusfilename[MAX_NAME+1];
   statusfilename[MAX_NAME] = '\0';
469
470
471
472
473
474
475
476

   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
477
478
}

479
#ifdef HAVE_TASKSTATS
480

Hisham Muhammad's avatar
Hisham Muhammad committed
481
static void ProcessList_readIoFile(Process* proc, const char* dirname, char* name) {
482
483
484
485
486
487
   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) {
488
489
      char buffer[256];
      buffer[255] = '\0';
490
491
492
493
494
      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
495
      while (fgets(buffer, 255, io)) {
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
         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);
   }
}
517

518
519
#endif

Hisham Muhammad's avatar
Hisham Muhammad committed
520
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, pid_t parentPid, float period) {
Hisham Muhammad's avatar
Hisham Muhammad committed
521
522
523
524
525
   DIR* dir;
   struct dirent* entry;
   Process* prototype = this->prototype;

   dir = opendir(dirname);
526
527
528
   if (!dir) return false;
   int processors = this->processorCount;
   bool showUserlandThreads = !this->hideUserlandThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
   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;
      }

545
      if (pid > 0) {
Hisham Muhammad's avatar
Hisham Muhammad committed
546

Hisham Muhammad's avatar
Hisham Muhammad committed
547
548
549
550
         FILE* status;
         char statusfilename[MAX_NAME+1];
         char command[PROCESS_COMM_LEN + 1];

551
         Process* process = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
552
         Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);
553

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

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

577
         #ifdef HAVE_TASKSTATS        
Hisham Muhammad's avatar
Hisham Muhammad committed
578
         ProcessList_readIoFile(process, dirname, name);
579
580
         #endif

Hisham Muhammad's avatar
Hisham Muhammad committed
581
582
         process->updated = true;

583
         if (!existingProcess)
Hisham Muhammad's avatar
Hisham Muhammad committed
584
            if (! ProcessList_readStatusFile(process, dirname, name))
585
586
               goto errorReadingProcess;

587
588
589
590
591
592
593
594
         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, 
595
             &process->m_trs, &process->m_lrs, &process->m_drs, 
596
597
598
599
600
601
602
603
604
             &process->m_dt);

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

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

605
606
607
608
609
         int lasttimes = (process->utime + process->stime);

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

Hisham Muhammad's avatar
Hisham Muhammad committed
613
         int success = ProcessList_readStatFile(process, status, command);
614
         fclose(status);
615
         if(!success) {
616
            goto errorReadingProcess;
617
         }
618

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

            #ifdef HAVE_OPENVZ
            if (access("/proc/vz", R_OK) != 0) {
               process->vpid = process->pid;
625
               process->ctid = 0;
626
627
628
629
630
631
632
633
634
635
636
637
            } 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 "
638
                  "%*u %*u %u %u",
639
                  &process->vpid, &process->ctid);
640
641
642
               fclose(status);
            }
            #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
643
644
645
646
647
648
649
650
651

            #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
652
               while (fgets(buffer, 255, status)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673

                  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
Hisham Muhammad's avatar
Hisham Muhammad committed
674
675
676
677
678
         }

         if ( ((!existingProcess) && (!showUserlandThreads || pid == parentPid || !this->showThreadNames))
              || (this->showingThreadNames && !this->showThreadNames) ) {

Hisham Muhammad's avatar
Hisham Muhammad committed
679
            snprintf(statusfilename, MAX_NAME, "%s/%s/cmdline", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
680
            status = ProcessList_fopen(this, statusfilename, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
681
682
683
            if (!status) {
               goto errorReadingProcess;
            }
Hisham Muhammad's avatar
Hisham Muhammad committed
684
            
Hisham Muhammad's avatar
Hisham Muhammad committed
685
686
687
688
689
690
691
            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
692
            fclose(status);
Hisham Muhammad's avatar
Hisham Muhammad committed
693
694
            command[PROCESS_COMM_LEN] = '\0';
            process->comm = String_copy(command);
Hisham Muhammad's avatar
Hisham Muhammad committed
695
696
         } else if (pid != parentPid && this->showThreadNames) {
            process->comm = String_copy(command);
Hisham Muhammad's avatar
Hisham Muhammad committed
697
698
         }

699
         int percent_cpu = (process->utime + process->stime - lasttimes) / 
700
            period * 100.0;
701
         process->percent_cpu = MAX(MIN(percent_cpu, processors*100.0), 0.0);
702

Hisham Muhammad's avatar
Hisham Muhammad committed
703
         process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / 
704
            (float)(this->totalMem) * 
Hisham Muhammad's avatar
Hisham Muhammad committed
705
706
707
            100.0;

         this->totalTasks++;
Hisham Muhammad's avatar
Hisham Muhammad committed
708
709
710
         if (process->state == 'R') {
            this->runningTasks++;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
711

712
         if (!existingProcess) {
713
714
            process = Process_clone(process);
            ProcessList_add(this, process);
715
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
716
717
718
719
720

         continue;

         // Exception handler.
         errorReadingProcess: {
721
722
723
724
            if (process->comm) {
               free(process->comm);
               process->comm = NULL;
            }
725
726
            if (existingProcess)
               ProcessList_remove(this, process);
727
            assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
728
729
730
731
         }
      }
   }
   closedir(dir);
732
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
733
734
735
}

void ProcessList_scan(ProcessList* this) {
736
   unsigned long long int usertime, nicetime, systemtime, systemalltime, idlealltime, idletime, totaltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
737
   unsigned long long int swapFree = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
738
739

   FILE* status;
Hisham Muhammad's avatar
Hisham Muhammad committed
740
   status = ProcessList_fopen(this, PROCMEMINFOFILE, "r");
Hisham Muhammad's avatar
Hisham Muhammad committed
741
   assert(status != NULL);
742
   int processors = this->processorCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
   {
      char buffer[128];
      while (fgets(buffer, 128, status)) {
   
         switch (buffer[0]) {
         case 'M':
            if (String_startsWith(buffer, "MemTotal:"))
               ProcessList_read(this, buffer, "MemTotal: %llu kB", &this->totalMem);
            else if (String_startsWith(buffer, "MemFree:"))
               ProcessList_read(this, buffer, "MemFree: %llu kB", &this->freeMem);
            else if (String_startsWith(buffer, "MemShared:"))
               ProcessList_read(this, buffer, "MemShared: %llu kB", &this->sharedMem);
            break;
         case 'B':
            if (String_startsWith(buffer, "Buffers:"))
               ProcessList_read(this, buffer, "Buffers: %llu kB", &this->buffersMem);
            break;
         case 'C':
            if (String_startsWith(buffer, "Cached:"))
               ProcessList_read(this, buffer, "Cached: %llu kB", &this->cachedMem);
            break;
         case 'S':
            if (String_startsWith(buffer, "SwapTotal:"))
               ProcessList_read(this, buffer, "SwapTotal: %llu kB", &this->totalSwap);
            if (String_startsWith(buffer, "SwapFree:"))
               ProcessList_read(this, buffer, "SwapFree: %llu kB", &swapFree);
            break;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
771
772
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
773

Hisham Muhammad's avatar
Hisham Muhammad committed
774
775
776
777
   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
   fclose(status);

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

Hisham Muhammad's avatar
Hisham Muhammad committed
780
   assert(status != NULL);
781
   for (int i = 0; i <= processors; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
782
783
      char buffer[256];
      int cpuid;
784
      unsigned long long int ioWait, irq, softIrq, steal;
Hisham Muhammad's avatar
Hisham Muhammad committed
785
786
787
788
789
790
      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)
791
         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
792
      else {
793
         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
794
795
796
797
         assert(cpuid == i - 1);
      }
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
798
799
800
      idlealltime = idletime + ioWait;
      systemalltime = systemtime + irq + softIrq + steal;
      totaltime = usertime + nicetime + systemalltime + idlealltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
801
802
803
804
805
      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]);
806
      assert (systemalltime >= this->systemAllTime[i]);
807
      assert (idlealltime >= this->idleAllTime[i]);
808
      assert (ioWait >= this->ioWaitTime[i]);
809
810
811
      assert (irq >= this->irqTime[i]);
      assert (softIrq >= this->softIrqTime[i]);
      assert (steal >= this->stealTime[i]);
Hisham Muhammad's avatar
Hisham Muhammad committed
812
813
814
      this->userPeriod[i] = usertime - this->userTime[i];
      this->nicePeriod[i] = nicetime - this->niceTime[i];
      this->systemPeriod[i] = systemtime - this->systemTime[i];
815
      this->systemAllPeriod[i] = systemalltime - this->systemAllTime[i];
816
      this->idleAllPeriod[i] = idlealltime - this->idleAllTime[i];
Hisham Muhammad's avatar
Hisham Muhammad committed
817
      this->idlePeriod[i] = idletime - this->idleTime[i];
818
819
820
821
      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
822
823
824
825
      this->totalPeriod[i] = totaltime - this->totalTime[i];
      this->userTime[i] = usertime;
      this->niceTime[i] = nicetime;
      this->systemTime[i] = systemtime;
826
      this->systemAllTime[i] = systemalltime;
827
      this->idleAllTime[i] = idlealltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
828
      this->idleTime[i] = idletime;
829
830
831
832
      this->ioWaitTime[i] = ioWait;
      this->irqTime[i] = irq;
      this->softIrqTime[i] = softIrq;
      this->stealTime[i] = steal;
Hisham Muhammad's avatar
Hisham Muhammad committed
833
834
      this->totalTime[i] = totaltime;
   }
835
   float period = (float)this->totalPeriod[0] / processors;
Hisham Muhammad's avatar
Hisham Muhammad committed
836
837
838
   fclose(status);

   // mark all process as "dirty"
839
840
   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
841
842
843
844
845
      p->updated = false;
   }
   
   this->totalTasks = 0;
   this->runningTasks = 0;
846

Hisham Muhammad's avatar
Hisham Muhammad committed
847
848
849
   ProcessList_processEntries(this, PROCDIR, NULL, 0, period);
   
   this->showingThreadNames = this->showThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
850
   
851
852
   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
853
854
855
856
857
858
859
      if (p->updated == false)
         ProcessList_remove(this, p);
      else
         p->updated = false;
   }

}
860
861
862
863
864
865
866
867
868
869
870
871
872
873

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;
}