ProcessList.c 26.5 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
#include <math.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
31
32
33
34
35

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

/*{
36

Hisham Muhammad's avatar
Hisham Muhammad committed
37
38
39
40
41
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif

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

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

#ifndef MAX_NAME
Hisham Muhammad's avatar
Hisham Muhammad committed
50
51
52
53
#define MAX_NAME 128
#endif

#ifndef MAX_READ
54
#define MAX_READ 2048
Hisham Muhammad's avatar
Hisham Muhammad committed
55
56
#endif

57
#ifndef ProcessList_cpuId
58
#define ProcessList_cpuId(pl, cpu) ((pl)->countCPUsFromZero ? (cpu) : (cpu)+1)
59
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
60
61
62
63
}*/

/*{

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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;

Hisham Muhammad's avatar
Hisham Muhammad committed
92
typedef struct ProcessList_ {
93
94
   Vector* processes;
   Vector* processes2;
Hisham Muhammad's avatar
Hisham Muhammad committed
95
96
97
   Hashtable* processTable;
   UsersTable* usersTable;

98
   int cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
99
   int totalTasks;
100
101
   int userlandThreads;
   int kernelThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
102
103
   int runningTasks;

104
105
106
107
   #ifdef HAVE_HWLOC
   hwloc_topology_t topology;
   bool topologyOk;
   #endif
108
   CPUData* cpus;
109
110
111
112
113
114
115
116
117
118

   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
119
120
121
122
123
124

   ProcessField* fields;
   ProcessField sortKey;
   int direction;
   bool hideThreads;
   bool shadowOtherUsers;
Hisham Muhammad's avatar
Hisham Muhammad committed
125
126
   bool showThreadNames;
   bool showingThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
127
128
129
130
131
   bool hideKernelThreads;
   bool hideUserlandThreads;
   bool treeView;
   bool highlightBaseName;
   bool highlightMegabytes;
132
   bool highlightThreads;
133
   bool detailedCPUTime;
134
   bool countCPUsFromZero;
Hisham Muhammad's avatar
Hisham Muhammad committed
135
136
137
138

} ProcessList;
}*/

139
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
140
141
142

ProcessList* ProcessList_new(UsersTable* usersTable) {
   ProcessList* this;
143
   this = calloc(sizeof(ProcessList), 1);
144
   this->processes = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
145
   this->processTable = Hashtable_new(70, false);
146
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
147
148
149
   this->usersTable = usersTable;
   
   /* tree-view auxiliary buffers */
150
   this->processes2 = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
151
   
152
153
   FILE* file = fopen(PROCSTATFILE, "r");
   assert(file != NULL);
Hisham Muhammad's avatar
Hisham Muhammad committed
154
   char buffer[256];
155
   int cpus = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
156
   do {
157
      cpus++;
158
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
159
   } while (String_startsWith(buffer, "cpu"));
160
   fclose(file);
161
   this->cpuCount = cpus - 1;
162
163
164
165
166
167
168
169
170

#ifdef HAVE_HWLOC
   this->topologyOk = false;
   int topoErr = hwloc_topology_init(&this->topology);
   if (topoErr == 0) {
      topoErr = hwloc_topology_load(this->topology);
      this->topologyOk = true;
   }
#endif
171
   this->cpus = calloc(sizeof(CPUData), cpus);
172

173
174
175
   for (int i = 0; i < cpus; i++) {
      this->cpus[i].totalTime = 1;
      this->cpus[i].totalPeriod = 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
176
177
178
   }

   this->fields = calloc(sizeof(ProcessField), LAST_PROCESSFIELD+1);
179
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
180
181
182
183
184
185
186
187
   // (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;
188
189
   this->showThreadNames = false;
   this->showingThreadNames = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
190
191
192
193
194
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
195
   this->detailedCPUTime = false;
196
   this->countCPUsFromZero = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
197
198
199
200
201
202

   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
203
204
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
205
   free(this->cpus);
Hisham Muhammad's avatar
Hisham Muhammad committed
206
207
208
209
210
211
212
213
214
215
216
   free(this->fields);
   free(this);
}

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

217
218
void ProcessList_printHeader(ProcessList* this, RichString* header) {
   RichString_prune(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
219
220
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
221
      const char* field = Process_fieldTitles[fields[i]];
Hisham Muhammad's avatar
Hisham Muhammad committed
222
      if (this->sortKey == fields[i])
223
         RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
224
      else
225
         RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
226
227
228
   }
}

229
static void ProcessList_add(ProcessList* this, Process* p) {
230
231
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
   assert(Hashtable_get(this->processTable, p->pid) == NULL);
232
   
233
   Vector_add(this->processes, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
234
   Hashtable_put(this->processTable, p->pid, p);
235
   
236
237
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
238
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
239
240
}

241
static void ProcessList_remove(ProcessList* this, Process* p) {
242
243
244
   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);
245
   assert(pp == p); (void)pp;
246
   unsigned int pid = p->pid;
Hisham Muhammad's avatar
Hisham Muhammad committed
247
248
249
   int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
   assert(idx != -1);
   if (idx >= 0) Vector_remove(this->processes, idx);
250
   assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
251
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
252
253
}

Hisham Muhammad's avatar
Hisham Muhammad committed
254
255
Process* ProcessList_get(ProcessList* this, int idx) {
   return (Process*) (Vector_get(this->processes, idx));
Hisham Muhammad's avatar
Hisham Muhammad committed
256
257
258
}

int ProcessList_size(ProcessList* this) {
259
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
260
261
}

Hisham Muhammad's avatar
Hisham Muhammad committed
262
static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) {
263
   Vector* children = Vector_new(PROCESS_CLASS, false, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
264

265
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
266
      Process* process = (Process*) (Vector_get(this->processes, i));
267
      if (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
268
         process = (Process*) (Vector_take(this->processes, i));
269
         Vector_add(children, process);
Hisham Muhammad's avatar
Hisham Muhammad committed
270
271
      }
   }
272
   int size = Vector_size(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
273
   for (int i = 0; i < size; i++) {
274
      Process* process = (Process*) (Vector_get(children, i));
275
276
277
278
279
280
281
282
283
      if (!show)
         process->show = false;
      int s = this->processes2->items;
      if (direction == 1)
         Vector_add(this->processes2, process);
      else
         Vector_insert(this->processes2, 0, process);
      assert(this->processes2->items == s+1); (void)s;
      int nextIndent = indent | (1 << level);
284
      ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
285
      process->indent = nextIndent;
Hisham Muhammad's avatar
Hisham Muhammad committed
286
   }
287
   Vector_delete(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
288
289
290
291
}

void ProcessList_sort(ProcessList* this) {
   if (!this->treeView) {
292
      Vector_sort(this->processes);
Hisham Muhammad's avatar
Hisham Muhammad committed
293
   } else {
294
      // Save settings
Hisham Muhammad's avatar
Hisham Muhammad committed
295
296
      int direction = this->direction;
      int sortKey = this->sortKey;
297
      // Sort by PID
Hisham Muhammad's avatar
Hisham Muhammad committed
298
299
      this->sortKey = PID;
      this->direction = 1;
300
      Vector_sort(this->processes);
301
      // Restore settings
Hisham Muhammad's avatar
Hisham Muhammad committed
302
303
      this->sortKey = sortKey;
      this->direction = direction;
304
      // Take PID 1 as root and add to the new listing
305
      int vsize = Vector_size(this->processes);
306
      Process* init = (Process*) (Vector_take(this->processes, 0));
307
308
309
      // 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
310
      init->indent = 0;
311
      Vector_add(this->processes2, init);
312
      // Recursively empty list
Hisham Muhammad's avatar
Hisham Muhammad committed
313
      ProcessList_buildTree(this, init->pid, 0, 0, direction, true);
314
      // Add leftovers
315
316
317
318
      while (Vector_size(this->processes)) {
         Process* p = (Process*) (Vector_take(this->processes, 0));
         p->indent = 0;
         Vector_add(this->processes2, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
319
         ProcessList_buildTree(this, p->pid, 0, 0, direction, p->showChildren);
320
321
322
      }
      assert(Vector_size(this->processes2) == vsize); (void)vsize;
      assert(Vector_size(this->processes) == 0);
323
      // Swap listings around
324
      Vector* t = this->processes;
Hisham Muhammad's avatar
Hisham Muhammad committed
325
326
327
328
329
      this->processes = this->processes2;
      this->processes2 = t;
   }
}

330
331
332
333
334
335
336
static bool ProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return false;

Hisham Muhammad's avatar
Hisham Muhammad committed
337
338
   static char buf[MAX_READ];

339
340
   int size = fread(buf, 1, MAX_READ, file);
   if (!size) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
341

342
   assert(process->pid == atoi(buf));
Hisham Muhammad's avatar
Hisham Muhammad committed
343
   char *location = strchr(buf, ' ');
344
   if (!location) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
345
346
347

   location += 2;
   char *end = strrchr(location, ')');
348
   if (!end) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
349
350
351
352
353
   
   int commsize = end - location;
   memcpy(command, location, commsize);
   command[commsize] = '\0';
   location = end + 2;
354
355
356
357
358

   int num = sscanf(location, 
      "%c %d %u %u %u "
      "%d %lu "
      "%*u %*u %*u %*u "
359
      "%llu %llu %llu %llu "
360
361
      "%ld %ld %ld "
      "%*d %*u %*u %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u "
362
      "%d %d",
363
364
365
366
367
368
369
      &process->state, &process->ppid, &process->pgrp, &process->session, &process->tty_nr, 
      &process->tpgid, &process->flags,
      &process->utime, &process->stime, &process->cutime, &process->cstime, 
      &process->priority, &process->nice, &process->nlwp,
      &process->exit_signal, &process->processor);
   fclose(file);
   return (num == 16);
Hisham Muhammad's avatar
Hisham Muhammad committed
370
371
}

372
373
374
static bool ProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
375

376
   snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
377
   struct stat sstat;
378
   int statok = stat(filename, &sstat);
379
380
   if (statok == -1)
      return false;
381
   process->st_uid = sstat.st_uid;
Hisham Muhammad's avatar
Hisham Muhammad committed
382
383
384
  
   struct tm date;
   time_t ctime = sstat.st_ctime;
385
   process->starttime_ctime = ctime;
Hisham Muhammad's avatar
Hisham Muhammad committed
386
   (void) localtime_r((time_t*) &ctime, &date);
387
   strftime(process->starttime_show, 7, ((ctime > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
Hisham Muhammad's avatar
Hisham Muhammad committed
388
   
389
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
390
391
}

392
#ifdef HAVE_TASKSTATS
393

394
395
396
static void ProcessList_readIoFile(Process* process, const char* dirname, char* name) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
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
427
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
480
481
482
483
484
485
486
487
488
489
   snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return;
   
   char buffer[256];
   buffer[255] = '\0';
   struct timeval tv;
   gettimeofday(&tv,NULL);
   unsigned long long now = tv.tv_sec*1000+tv.tv_usec/1000;
   unsigned long long last_read = process->io_read_bytes;
   unsigned long long last_write = process->io_write_bytes;
   while (fgets(buffer, 255, file)) {
      if (sscanf(buffer, "rchar: %llu", &process->io_rchar)) continue;
      if (sscanf(buffer, "wchar: %llu", &process->io_wchar)) continue;
      if (sscanf(buffer, "syscr: %llu", &process->io_syscr)) continue;
      if (sscanf(buffer, "syscw: %llu", &process->io_syscw)) continue;
      if (sscanf(buffer, "read_bytes: %llu", &process->io_read_bytes)) {
         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;
         continue;
      }
      if (sscanf(buffer, "write_bytes: %llu", &process->io_write_bytes)) {
         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;
         continue;
      }
      sscanf(buffer, "cancelled_write_bytes: %llu", &process->io_cancelled_write_bytes);
   }
   fclose(file);
}

#endif

static bool ProcessList_readStatmFile(Process* process, const char* dirname, const char* name) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return false;

   int num = fscanf(file, "%d %d %d %d %d %d %d",
       &process->m_size, &process->m_resident, &process->m_share, 
       &process->m_trs, &process->m_lrs, &process->m_drs, 
       &process->m_dt);
   fclose(file);
   return (num == 7);
}

#ifdef HAVE_OPENVZ

static void ProcessList_readOpenVZData(Process* process, const char* dirname, const char* name) {
   if (access("/proc/vz", R_OK) != 0) {
      process->vpid = process->pid;
      process->ctid = 0;
      return;
   }
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file) 
      return;
   fscanf(file, 
      "%*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 "
      "%*u %*u %u %u",
      &process->vpid, &process->ctid);
   fclose(file);
}

#endif

#ifdef HAVE_CGROUP

static void ProcessList_readCGroupFile(Process* process, const char* dirname, const char* name) {
   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;
   }
   char buffer[256];
   char *ok = fgets(buffer, 255, file);
   if (ok) {
      char* trimmed = String_trim(buffer);
490
491
      int nFields;
      char** fields = String_split(trimmed, ':', &nFields);
492
      free(trimmed);
493
494
495
496
497
      if (nFields >= 3) {
         process->cgroup = strndup(fields[2] + 1, 10);
      } else {
         process->cgroup = strdup("");
      }
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
      String_freeArray(fields);
   }
   fclose(file);
}

#endif

#ifdef HAVE_VSERVER

static void ProcessList_readVServerData(Process* process, const char* dirname, const char* name) {
   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%d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
521
         }
522
523
524
525
526
527
528
      }
      #if defined HAVE_ANCIENT_VSERVER
      else if (String_startsWith(buffer, "s_context:")) {
         int vxid;
         int ok = sscanf(buffer, "s_context:\t%d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
529
530
         }
      }
531
      #endif
532
   }
533
   fclose(file);
534
}
535

536
537
#endif

538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
static bool ProcessList_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);
   FILE* file = fopen(filename, "r");
   if (!file)
      return false;
         
   char command[4096+1]; // max cmdline length on Linux
   int amtRead = fread(command, 1, sizeof(command) - 1, file);
   if (amtRead > 0) {
      for (int i = 0; i < amtRead; i++)
         if (command[i] == '\0' || command[i] == '\n') {
            command[i] = ' ';
         }
   }
   command[amtRead] = '\0';
   fclose(file);
   free(process->comm);
   process->comm = String_copy(command);
   return true;
}


Hisham Muhammad's avatar
Hisham Muhammad committed
563
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, double period) {
Hisham Muhammad's avatar
Hisham Muhammad committed
564
565
566
567
   DIR* dir;
   struct dirent* entry;

   dir = opendir(dirname);
568
   if (!dir) return false;
569
   int cpus = this->cpuCount;
570
571
   bool hideKernelThreads = this->hideKernelThreads;
   bool hideUserlandThreads = this->hideUserlandThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
572
573
574
   while ((entry = readdir(dir)) != NULL) {
      char* name = entry->d_name;
      // filename is a number: process directory
575
576
577
      int pid = atoi(name);
     
      if (parent && pid == parent->pid)
578
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
579
580
581
582

      // The RedHat kernel hides threads with a dot.
      // I believe this is non-standard.
      if ((!this->hideThreads) && pid == 0 && name[0] == '.') {
Hisham Muhammad's avatar
Hisham Muhammad committed
583
         pid = atoi(name + 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
584
      }
585
586
      if (pid <= 0) 
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
587

588
589
590
591
592
593
594
595
596
597
598
      Process* process = NULL;
      Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);

      if (existingProcess) {
         assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1);
         process = existingProcess;
         assert(process->pid == pid);
      } else {
         process = Process_new(this);
         assert(process->comm == NULL);
         process->pid = pid;
599
         process->tgid = parent ? parent->pid : pid;
600
      }
601

602
603
604
      char subdirname[MAX_NAME+1];
      snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
      ProcessList_processEntries(this, subdirname, process, period);
605

606
607
608
      #ifdef HAVE_TASKSTATS
      ProcessList_readIoFile(process, dirname, name);
      #endif
609

610
611
      if (! ProcessList_readStatmFile(process, dirname, name))
         goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
612

613
      process->show = ! ((hideKernelThreads && Process_isKernelThread(process)) || (hideUserlandThreads && Process_isUserlandThread(process)));
614

615
      char command[MAX_NAME+1];
Hisham Muhammad's avatar
Hisham Muhammad committed
616
      unsigned long long int lasttimes = (process->utime + process->stime);
617
618
619
620
621
      if (! ProcessList_readStatFile(process, dirname, name, command))
         goto errorReadingProcess;
      int percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0;
      process->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0);
      if (isnan(process->percent_cpu)) process->percent_cpu = 0.0;
Hisham Muhammad's avatar
Hisham Muhammad committed
622
      process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
623

624
      if(!existingProcess) {
625

626
         if (! ProcessList_statProcessDir(process, dirname, name))
627
628
            goto errorReadingProcess;

629
630
         process->user = UsersTable_getRef(this->usersTable, process->st_uid);

631
632
633
         #ifdef HAVE_OPENVZ
         ProcessList_readOpenVZData(process, dirname, name);
         #endif
634

635
636
637
         #ifdef HAVE_CGROUP
         ProcessList_readCGroupFile(process, dirname, name);
         #endif
638
         
639
640
641
642
643
         #ifdef HAVE_VSERVER
         ProcessList_readVServerData(process, dirname, name);
         #endif
         
         if (! ProcessList_readCmdlineFile(process, dirname, name))
644
645
            goto errorReadingProcess;

646
647
         ProcessList_add(this, process);
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
648

649
650
651
652
      if (process->state == 'Z') {
         free(process->comm);
         process->comm = String_copy(command);
      } else if (Process_isThread(process)) {
653
         if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') {
654
            free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
655
            process->comm = String_copy(command);
656
657
658
         } else if (this->showingThreadNames) {
            if (! ProcessList_readCmdlineFile(process, dirname, name))
               goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
659
         }
660
661
662
663
664
         if (Process_isKernelThread(process)) {
            this->kernelThreads++;
         } else {
            this->userlandThreads++;
         }
665
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
666

667
668
669
670
      this->totalTasks++;
      if (process->state == 'R')
         this->runningTasks++;
      process->updated = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
671

672
      continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
673

674
675
676
677
678
      // Exception handler.
      errorReadingProcess: {
         if (process->comm) {
            free(process->comm);
            process->comm = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
679
         }
680
681
682
683
         if (existingProcess)
            ProcessList_remove(this, process);
         else
            Process_delete((Object*)process);
Hisham Muhammad's avatar
Hisham Muhammad committed
684
685
686
      }
   }
   closedir(dir);
687
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
688
689
690
}

void ProcessList_scan(ProcessList* this) {
691
   unsigned long long int usertime, nicetime, systemtime, systemalltime, idlealltime, idletime, totaltime, virtalltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
692
   unsigned long long int swapFree = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
693

694
695
   FILE* file = fopen(PROCMEMINFOFILE, "r");
   assert(file != NULL);
696
   int cpus = this->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
697
698
   {
      char buffer[128];
699
      while (fgets(buffer, 128, file)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
700
701
702
703
   
         switch (buffer[0]) {
         case 'M':
            if (String_startsWith(buffer, "MemTotal:"))
704
               sscanf(buffer, "MemTotal: %llu kB", &this->totalMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
705
            else if (String_startsWith(buffer, "MemFree:"))
706
               sscanf(buffer, "MemFree: %llu kB", &this->freeMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
707
            else if (String_startsWith(buffer, "MemShared:"))
708
               sscanf(buffer, "MemShared: %llu kB", &this->sharedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
709
710
711
            break;
         case 'B':
            if (String_startsWith(buffer, "Buffers:"))
712
               sscanf(buffer, "Buffers: %llu kB", &this->buffersMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
713
714
715
            break;
         case 'C':
            if (String_startsWith(buffer, "Cached:"))
716
               sscanf(buffer, "Cached: %llu kB", &this->cachedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
717
718
719
            break;
         case 'S':
            if (String_startsWith(buffer, "SwapTotal:"))
720
               sscanf(buffer, "SwapTotal: %llu kB", &this->totalSwap);
Hisham Muhammad's avatar
Hisham Muhammad committed
721
            if (String_startsWith(buffer, "SwapFree:"))
722
               sscanf(buffer, "SwapFree: %llu kB", &swapFree);
Hisham Muhammad's avatar
Hisham Muhammad committed
723
724
            break;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
725
726
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
727

Hisham Muhammad's avatar
Hisham Muhammad committed
728
729
   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
730
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
731

732
733
   file = fopen(PROCSTATFILE, "r");
   assert(file != NULL);
734
   for (int i = 0; i <= cpus; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
735
736
      char buffer[256];
      int cpuid;
737
738
      unsigned long long int ioWait, irq, softIrq, steal, guest;
      ioWait = irq = softIrq = steal = guest = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
739
740
741
      // Dependending on your kernel version,
      // 5, 7 or 8 of these fields will be set.
      // The rest will remain at zero.
742
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
743
      if (i == 0)
744
         sscanf(buffer, "cpu  %llu %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
Hisham Muhammad's avatar
Hisham Muhammad committed
745
      else {
746
         sscanf(buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
Hisham Muhammad's avatar
Hisham Muhammad committed
747
748
749
750
         assert(cpuid == i - 1);
      }
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
751
      idlealltime = idletime + ioWait;
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
      systemalltime = systemtime + irq + softIrq;
      virtalltime = steal + guest;
      totaltime = usertime + nicetime + systemalltime + idlealltime + 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 (guest >= 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 = guest - 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 = guest;
      cpuData->totalTime = totaltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
792
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
793
   double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
794
795

   // mark all process as "dirty"
796
797
   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
798
799
800
801
      p->updated = false;
   }
   
   this->totalTasks = 0;
802
803
   this->userlandThreads = 0;
   this->kernelThreads = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
804
   this->runningTasks = 0;
805

806
   ProcessList_processEntries(this, PROCDIR, NULL, period);
Hisham Muhammad's avatar
Hisham Muhammad committed
807
808
   
   this->showingThreadNames = this->showThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
809
   
810
811
   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
812
813
814
815
816
817
818
      if (p->updated == false)
         ProcessList_remove(this, p);
      else
         p->updated = false;
   }

}
819
820
821
822
823
824
825
826
827
828
829
830
831
832

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;
}
833
834
835
836
837
838
839
840

void ProcessList_expandTree(ProcessList* this) {
   int size = Vector_size(this->processes);
   for (int i = 0; i < size; i++) {
      Process* process = (Process*) Vector_get(this->processes, i);
      process->showChildren = true;
   }
}