ProcessList.c 12.1 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
3
4
5
6
7
8
/*
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.
*/

#include "ProcessList.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
9
10

#include "CRT.h"
11
#include "String.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12
13

#include <stdlib.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
14
#include <string.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
15
16

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
17
18
19
20
21
#include "Vector.h"
#include "Hashtable.h"
#include "UsersTable.h"
#include "Panel.h"
#include "Process.h"
22

Hisham Muhammad's avatar
Hisham Muhammad committed
23
#ifndef MAX_NAME
Hisham Muhammad's avatar
Hisham Muhammad committed
24
25
26
27
#define MAX_NAME 128
#endif

#ifndef MAX_READ
28
#define MAX_READ 2048
Hisham Muhammad's avatar
Hisham Muhammad committed
29
30
#endif

31
#ifndef ProcessList_cpuId
32
#define ProcessList_cpuId(pl, cpu) ((pl)->countCPUsFromZero ? (cpu) : (cpu)+1)
33
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
34

35
36
37
38
39
40
41
42
43
44
45
typedef enum TreeStr_ {
   TREE_STR_HORZ,
   TREE_STR_VERT,
   TREE_STR_RTEE,
   TREE_STR_BEND,
   TREE_STR_TEND,
   TREE_STR_OPEN,
   TREE_STR_SHUT,
   TREE_STR_COUNT
} TreeStr;

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
74
typedef struct ProcessList_ {
Hisham Muhammad's avatar
Hisham Muhammad committed
75
   const char **treeStr;
76
77
   Vector* processes;
   Vector* processes2;
Hisham Muhammad's avatar
Hisham Muhammad committed
78
79
80
   Hashtable* processTable;
   UsersTable* usersTable;

81
   Panel* panel;
82
   int following;
83
84
   uid_t userId;
   const char* incFilter;
85
   Hashtable* pidWhiteList;
86

87
   int cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
88
   int totalTasks;
89
90
   int userlandThreads;
   int kernelThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
91
92
   int runningTasks;

93
   #ifdef HAVE_LIBHWLOC
94
95
96
   hwloc_topology_t topology;
   bool topologyOk;
   #endif
97
   CPUData* cpus;
98
99
100
101
102
103
104
105
106
107

   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
108

109
   int flags;
Hisham Muhammad's avatar
Hisham Muhammad committed
110
111
112
113
114
   ProcessField* fields;
   ProcessField sortKey;
   int direction;
   bool hideThreads;
   bool shadowOtherUsers;
Hisham Muhammad's avatar
Hisham Muhammad committed
115
116
   bool showThreadNames;
   bool showingThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
117
118
119
120
121
   bool hideKernelThreads;
   bool hideUserlandThreads;
   bool treeView;
   bool highlightBaseName;
   bool highlightMegabytes;
122
   bool highlightThreads;
123
   bool detailedCPUTime;
124
   bool countCPUsFromZero;
125
   bool updateProcessNames;
126
   bool accountGuestInCPUMeter;
Hisham Muhammad's avatar
Hisham Muhammad committed
127
   bool userOnly;
Hisham Muhammad's avatar
Hisham Muhammad committed
128
129

} ProcessList;
130

131
132
133
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList);
void ProcessList_scan(ProcessList* pl);

Hisham Muhammad's avatar
Hisham Muhammad committed
134
135
}*/

136
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
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
const char *ProcessList_treeStrAscii[TREE_STR_COUNT] = {
   "-", // TREE_STR_HORZ
   "|", // TREE_STR_VERT
   "`", // TREE_STR_RTEE
   "`", // TREE_STR_BEND
   ",", // TREE_STR_TEND
   "+", // TREE_STR_OPEN
   "-", // TREE_STR_SHUT
};

const char *ProcessList_treeStrUtf8[TREE_STR_COUNT] = {
   "\xe2\x94\x80", // TREE_STR_HORZ ─
   "\xe2\x94\x82", // TREE_STR_VERT │
   "\xe2\x94\x9c", // TREE_STR_RTEE ├
   "\xe2\x94\x94", // TREE_STR_BEND └
   "\xe2\x94\x8c", // TREE_STR_TEND ┌
   "+",            // TREE_STR_OPEN +
   "\xe2\x94\x80", // TREE_STR_SHUT ─
};

158
ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList) {
159
   this->processes = Vector_new(Class(Process), true, DEFAULT_SIZE);
160
   this->processTable = Hashtable_new(140, false);
Hisham Muhammad's avatar
Hisham Muhammad committed
161
   this->usersTable = usersTable;
162
   this->pidWhiteList = pidWhiteList;
Hisham Muhammad's avatar
Hisham Muhammad committed
163
   
164
   // tree-view auxiliary buffers
165
   this->processes2 = Vector_new(Class(Process), true, DEFAULT_SIZE);
Hisham Muhammad's avatar
Hisham Muhammad committed
166
   
167
168
169
   // set later by platform-specific code
   this->cpuCount = 0;
   this->cpus = NULL;
170

171
#ifdef HAVE_LIBHWLOC
172
173
174
175
   this->topologyOk = false;
   int topoErr = hwloc_topology_init(&this->topology);
   if (topoErr == 0) {
      topoErr = hwloc_topology_load(this->topology);
Hisham Muhammad's avatar
Hisham Muhammad committed
176
177
   }
   if (topoErr == 0) {
178
179
180
      this->topologyOk = true;
   }
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
181

182
   this->fields = calloc(LAST_PROCESSFIELD+1, sizeof(ProcessField));
183
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
184
   // (and ProcessFields into proper objects).
185
   this->flags = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
186
187
   for (int i = 0; defaultHeaders[i]; i++) {
      this->fields[i] = defaultHeaders[i];
188
      this->flags |= Process_fieldFlags[defaultHeaders[i]];
Hisham Muhammad's avatar
Hisham Muhammad committed
189
   }
190

Hisham Muhammad's avatar
Hisham Muhammad committed
191
192
193
194
   this->sortKey = PERCENT_CPU;
   this->direction = 1;
   this->hideThreads = false;
   this->shadowOtherUsers = false;
195
196
   this->showThreadNames = false;
   this->showingThreadNames = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
197
198
199
200
201
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
202
   this->detailedCPUTime = false;
203
   this->countCPUsFromZero = false;
204
   this->updateProcessNames = false;
205
   this->treeStr = NULL;
206
   this->following = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
207

208
209
210
   if (CRT_utf8)
      this->treeStr = CRT_utf8 ? ProcessList_treeStrUtf8 : ProcessList_treeStrAscii;

Hisham Muhammad's avatar
Hisham Muhammad committed
211
212
213
214
215
   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
216
217
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
218
   free(this->cpus);
Hisham Muhammad's avatar
Hisham Muhammad committed
219
220
221
222
   free(this->fields);
   free(this);
}

223
224
225
226
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
   this->panel = panel;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
227
228
229
230
231
232
233
void ProcessList_invertSortOrder(ProcessList* this) {
   if (this->direction == 1)
      this->direction = -1;
   else
      this->direction = 1;
}

234
235
void ProcessList_printHeader(ProcessList* this, RichString* header) {
   RichString_prune(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
236
237
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
238
      const char* field = Process_fieldTitles[fields[i]];
239
      if (!this->treeView && this->sortKey == fields[i])
240
         RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
241
      else
242
         RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
243
244
245
   }
}

246
void ProcessList_add(ProcessList* this, Process* p) {
247
248
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
   assert(Hashtable_get(this->processTable, p->pid) == NULL);
249
   
250
   Vector_add(this->processes, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
251
   Hashtable_put(this->processTable, p->pid, p);
252
   
253
254
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
255
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
256
257
}

258
void ProcessList_remove(ProcessList* this, Process* p) {
259
260
261
   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);
262
   assert(pp == p); (void)pp;
263
   unsigned int pid = p->pid;
Hisham Muhammad's avatar
Hisham Muhammad committed
264
265
266
   int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
   assert(idx != -1);
   if (idx >= 0) Vector_remove(this->processes, idx);
267
   assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
268
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
269
270
}

Hisham Muhammad's avatar
Hisham Muhammad committed
271
272
Process* ProcessList_get(ProcessList* this, int idx) {
   return (Process*) (Vector_get(this->processes, idx));
Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
}

int ProcessList_size(ProcessList* this) {
276
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
277
278
}

Hisham Muhammad's avatar
Hisham Muhammad committed
279
static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) {
280
   Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
Hisham Muhammad's avatar
Hisham Muhammad committed
281

282
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
283
      Process* process = (Process*) (Vector_get(this->processes, i));
284
      if (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
285
         process = (Process*) (Vector_take(this->processes, i));
286
         Vector_add(children, process);
Hisham Muhammad's avatar
Hisham Muhammad committed
287
288
      }
   }
289
   int size = Vector_size(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
290
   for (int i = 0; i < size; i++) {
291
      Process* process = (Process*) (Vector_get(children, i));
292
293
294
295
296
297
298
299
300
      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);
301
      ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
302
303
304
305
      if (i == size - 1)
         process->indent = -nextIndent;
      else
         process->indent = nextIndent;
Hisham Muhammad's avatar
Hisham Muhammad committed
306
   }
307
   Vector_delete(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
308
309
310
311
}

void ProcessList_sort(ProcessList* this) {
   if (!this->treeView) {
312
      Vector_insertionSort(this->processes);
Hisham Muhammad's avatar
Hisham Muhammad committed
313
   } else {
314
      // Save settings
Hisham Muhammad's avatar
Hisham Muhammad committed
315
316
      int direction = this->direction;
      int sortKey = this->sortKey;
317
      // Sort by PID
Hisham Muhammad's avatar
Hisham Muhammad committed
318
319
      this->sortKey = PID;
      this->direction = 1;
320
      Vector_quickSort(this->processes);
321
      // Restore settings
Hisham Muhammad's avatar
Hisham Muhammad committed
322
323
      this->sortKey = sortKey;
      this->direction = direction;
324
      // Take PID 1 as root and add to the new listing
325
      int vsize = Vector_size(this->processes);
326
      Process* init = (Process*) (Vector_take(this->processes, 0));
327
      if (!init) return;
328
329
330
      // 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
331
      init->indent = 0;
332
      Vector_add(this->processes2, init);
333
      // Recursively empty list
Hisham Muhammad's avatar
Hisham Muhammad committed
334
      ProcessList_buildTree(this, init->pid, 0, 0, direction, true);
335
      // Add leftovers
336
337
338
339
      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
340
         ProcessList_buildTree(this, p->pid, 0, 0, direction, p->showChildren);
341
342
343
      }
      assert(Vector_size(this->processes2) == vsize); (void)vsize;
      assert(Vector_size(this->processes) == 0);
344
      // Swap listings around
345
      Vector* t = this->processes;
Hisham Muhammad's avatar
Hisham Muhammad committed
346
347
348
349
350
      this->processes = this->processes2;
      this->processes2 = t;
   }
}

351
352
353
354
355
356
357
358
359
360
361
362
363
364

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;
}
365
366
367
368
369
370
371
372

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

Hisham Muhammad's avatar
Hisham Muhammad committed
374
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, const char* incFilter) {
375
   if (!flags) {
376
      following = this->following;
377
378
      incFilter = this->incFilter;
   } else {
379
      this->following = following;
380
381
382
383
      this->incFilter = incFilter;
   }

   int currPos = Panel_getSelectedIndex(this->panel);
384
   pid_t currPid = following != -1 ? following : 0;
385
386
387
388
389
390
391
392
393
394
   int currScrollV = this->panel->scrollV;

   Panel_prune(this->panel);
   int size = ProcessList_size(this);
   int idx = 0;
   for (int i = 0; i < size; i++) {
      bool hidden = false;
      Process* p = ProcessList_get(this, i);

      if ( (!p->show)
Hisham Muhammad's avatar
Hisham Muhammad committed
395
         || (this->userOnly && (p->st_uid != this->userId))
396
         || (incFilter && !(String_contains_i(p->comm, incFilter)))
397
         || (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->pid)) )
398
399
400
401
         hidden = true;

      if (!hidden) {
         Panel_set(this->panel, idx, (Object*)p);
402
         if ((following == -1 && idx == currPos) || (following != -1 && p->pid == currPid)) {
403
404
405
406
407
408
409
            Panel_setSelected(this->panel, idx);
            this->panel->scrollV = currScrollV;
         }
         idx++;
      }
   }
}
410