1 /*
2 htop - ProcessList.c
3 (C) 2004,2005 Hisham H. Muhammad
4 Released under the GNU GPLv2+, see the COPYING file
5 in the source distribution for its full text.
6 */
7
8 #include "ProcessList.h"
9
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "CRT.h"
15 #include "DynamicColumn.h"
16 #include "Hashtable.h"
17 #include "Macros.h"
18 #include "Platform.h"
19 #include "Vector.h"
20 #include "XUtils.h"
21
22
ProcessList_init(ProcessList * this,const ObjectClass * klass,UsersTable * usersTable,Hashtable * dynamicMeters,Hashtable * dynamicColumns,Hashtable * pidMatchList,uid_t userId)23 ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
24 this->processes = Vector_new(klass, true, DEFAULT_SIZE);
25 this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
26
27 this->processTable = Hashtable_new(200, false);
28 this->displayTreeSet = Hashtable_new(200, false);
29 this->draftingTreeSet = Hashtable_new(200, false);
30
31 this->usersTable = usersTable;
32 this->pidMatchList = pidMatchList;
33 this->dynamicMeters = dynamicMeters;
34 this->dynamicColumns = dynamicColumns;
35
36 this->userId = userId;
37
38 // set later by platform-specific code
39 this->activeCPUs = 0;
40 this->existingCPUs = 0;
41 this->monotonicMs = 0;
42
43 // always maintain valid realtime timestamps
44 Platform_gettime_realtime(&this->realtime, &this->realtimeMs);
45
46 #ifdef HAVE_LIBHWLOC
47 this->topologyOk = false;
48 if (hwloc_topology_init(&this->topology) == 0) {
49 this->topologyOk =
50 #if HWLOC_API_VERSION < 0x00020000
51 /* try to ignore the top-level machine object type */
52 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) &&
53 /* ignore caches, which don't add structure */
54 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) &&
55 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) &&
56 0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) &&
57 #else
58 0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) &&
59 #endif
60 0 == hwloc_topology_load(this->topology);
61 }
62 #endif
63
64 this->following = -1;
65
66 return this;
67 }
68
ProcessList_done(ProcessList * this)69 void ProcessList_done(ProcessList* this) {
70 #ifdef HAVE_LIBHWLOC
71 if (this->topologyOk) {
72 hwloc_topology_destroy(this->topology);
73 }
74 #endif
75
76 Hashtable_delete(this->draftingTreeSet);
77 Hashtable_delete(this->displayTreeSet);
78 Hashtable_delete(this->processTable);
79
80 Vector_delete(this->processes2);
81 Vector_delete(this->processes);
82 }
83
ProcessList_setPanel(ProcessList * this,Panel * panel)84 void ProcessList_setPanel(ProcessList* this, Panel* panel) {
85 this->panel = panel;
86 }
87
alignedDynamicColumnTitle(const ProcessList * this,int key)88 static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
89 const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
90 if (column == NULL)
91 return "- ";
92 static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
93 int width = column->width;
94 if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
95 width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
96 xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
97 return titleBuffer;
98 }
99
alignedProcessFieldTitle(const ProcessList * this,ProcessField field)100 static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
101 if (field >= LAST_PROCESSFIELD)
102 return alignedDynamicColumnTitle(this, field);
103
104 const char* title = Process_fields[field].title;
105 if (!title)
106 return "- ";
107
108 if (Process_fields[field].pidColumn) {
109 static char titleBuffer[PROCESS_MAX_PID_DIGITS + sizeof(" ")];
110 xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
111 return titleBuffer;
112 }
113
114 if (field == ST_UID) {
115 static char titleBuffer[PROCESS_MAX_UID_DIGITS + sizeof(" ")];
116 xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
117 return titleBuffer;
118 }
119
120 return title;
121 }
122
ProcessList_printHeader(const ProcessList * this,RichString * header)123 void ProcessList_printHeader(const ProcessList* this, RichString* header) {
124 RichString_rewind(header, RichString_size(header));
125
126 const Settings* settings = this->settings;
127 const ProcessField* fields = settings->fields;
128
129 ProcessField key = Settings_getActiveSortKey(settings);
130
131 for (int i = 0; fields[i]; i++) {
132 int color;
133 if (settings->treeView && settings->treeViewAlwaysByPID) {
134 color = CRT_colors[PANEL_HEADER_FOCUS];
135 } else if (key == fields[i]) {
136 color = CRT_colors[PANEL_SELECTION_FOCUS];
137 } else {
138 color = CRT_colors[PANEL_HEADER_FOCUS];
139 }
140
141 RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i]));
142 if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
143 RichString_rewind(header, 1); // rewind to override space
144 RichString_appendnWide(header,
145 CRT_colors[PANEL_SELECTION_FOCUS],
146 CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC],
147 1);
148 }
149 if (COMM == fields[i] && settings->showMergedCommand) {
150 RichString_appendAscii(header, color, "(merged)");
151 }
152 }
153 }
154
ProcessList_add(ProcessList * this,Process * p)155 void ProcessList_add(ProcessList* this, Process* p) {
156 assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
157 assert(Hashtable_get(this->processTable, p->pid) == NULL);
158 p->processList = this;
159
160 // highlighting processes found in first scan by first scan marked "far in the past"
161 p->seenStampMs = this->monotonicMs;
162
163 Vector_add(this->processes, p);
164 Hashtable_put(this->processTable, p->pid, p);
165
166 assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
167 assert(Hashtable_get(this->processTable, p->pid) != NULL);
168 assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
169 }
170
ProcessList_remove(ProcessList * this,const Process * p)171 void ProcessList_remove(ProcessList* this, const Process* p) {
172 assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
173 assert(Hashtable_get(this->processTable, p->pid) != NULL);
174
175 const Process* pp = Hashtable_remove(this->processTable, p->pid);
176 assert(pp == p); (void)pp;
177
178 pid_t pid = p->pid;
179 int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
180 assert(idx != -1);
181
182 if (idx >= 0) {
183 Vector_remove(this->processes, idx);
184 }
185
186 if (this->following != -1 && this->following == pid) {
187 this->following = -1;
188 Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
189 }
190
191 assert(Hashtable_get(this->processTable, pid) == NULL);
192 assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
193 }
194
195 // ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
196 // relying only on itself.
197 //
198 // Algorithm
199 //
200 // The algorithm is based on `depth-first search`,
201 // even though `breadth-first search` approach may be more efficient on first glance,
202 // after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
203 // If it would be safe that approach would likely bring an advantage in performance.
204 //
205 // Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
206 // First it sorts a list. Then it runs the function recursively for each element of the sorted list.
207 // After that it updates the settings of processes.
208 //
209 // It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
210 //
211 // It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
212 // and at the same time make changes in it.
213 //
ProcessList_updateTreeSetLayer(ProcessList * this,unsigned int leftBound,unsigned int rightBound,unsigned int deep,unsigned int left,unsigned int right,unsigned int * index,unsigned int * treeIndex,int indent)214 static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) {
215
216 // It's guaranteed that layer_size is enough space
217 // but most likely it needs less. Specifically on first iteration.
218 int layerSize = (right - left) / 2;
219
220 // check if we reach `children` of `leaves`
221 if (layerSize == 0)
222 return;
223
224 Vector* layer = Vector_new(Vector_type(this->processes), false, layerSize);
225
226 // Find all processes on the same layer (process with the same `deep` value
227 // and included in a range from `leftBound` to `rightBound`).
228 //
229 // This loop also keeps track of left_bound and right_bound of these processes
230 // in order not to lose this information once the list is sorted.
231 //
232 // The variables left_bound and right_bound are different from what the values lhs and rhs represent.
233 // While left_bound and right_bound define a range of processes to look at, the values given by lhs and rhs are indices into an array
234 //
235 // In the below example note how filtering a range of indices i is different from filtering for processes in the bounds left_bound < x < right_bound …
236 //
237 // The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function.
238 //
239 // i | l | r
240 // 1 | 1 | 9
241 // 2 | 2 | 8
242 // 3 | 4 | 5
243 // 4 | 6 | 7
244 for (unsigned int i = leftBound; i < rightBound; i++) {
245 Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i);
246 assert(proc);
247 if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
248 if (Vector_size(layer) > 0) {
249 Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
250
251 // Make a 'right_bound' of previous_process in a layer the current process's index.
252 //
253 // Use 'tree_depth' as a temporal variable.
254 // It's safe to do as later 'tree_depth' will be renovated.
255 previous_process->tree_depth = proc->tree_index;
256 }
257
258 Vector_add(layer, proc);
259 }
260 }
261
262 // The loop above changes just up to process-1.
263 // So the last process of the layer isn't updated by the above code.
264 //
265 // Thus, if present, set the `rightBound` to the last process on the layer
266 if (Vector_size(layer) > 0) {
267 Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
268 previous_process->tree_depth = rightBound;
269 }
270
271 Vector_quickSort(layer);
272
273 int size = Vector_size(layer);
274 for (int i = 0; i < size; i++) {
275 Process* proc = (Process*)Vector_get(layer, i);
276
277 unsigned int idx = (*index)++;
278 int newLeft = (*treeIndex)++;
279
280 int level = deep == 0 ? 0 : (int)deep - 1;
281 int currentIndent = indent == -1 ? 0 : indent | (1 << level);
282 int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent);
283
284 unsigned int newLeftBound = proc->tree_index;
285 unsigned int newRightBound = proc->tree_depth;
286 ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent);
287
288 int newRight = (*treeIndex)++;
289
290 proc->tree_left = newLeft;
291 proc->tree_right = newRight;
292 proc->tree_index = idx;
293 proc->tree_depth = deep;
294
295 if (indent == -1) {
296 proc->indent = 0;
297 } else if (i == size - 1) {
298 proc->indent = -currentIndent;
299 } else {
300 proc->indent = currentIndent;
301 }
302
303 Hashtable_put(this->draftingTreeSet, proc->tree_index, proc);
304
305 // It's not strictly necessary to do this, but doing so anyways
306 // allows for checking the correctness of the inner workings.
307 Hashtable_remove(this->displayTreeSet, newLeftBound);
308 }
309
310 Vector_delete(layer);
311 }
312
ProcessList_updateTreeSet(ProcessList * this)313 static void ProcessList_updateTreeSet(ProcessList* this) {
314 unsigned int index = 0;
315 unsigned int tree_index = 1;
316
317 const int vsize = Vector_size(this->processes);
318
319 assert(Hashtable_count(this->draftingTreeSet) == 0);
320 assert((int)Hashtable_count(this->displayTreeSet) == vsize);
321
322 ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1);
323
324 Hashtable* tmp = this->draftingTreeSet;
325 this->draftingTreeSet = this->displayTreeSet;
326 this->displayTreeSet = tmp;
327
328 assert(Hashtable_count(this->draftingTreeSet) == 0);
329 assert((int)Hashtable_count(this->displayTreeSet) == vsize);
330 }
331
ProcessList_buildTreeBranch(ProcessList * this,pid_t pid,int level,int indent,int direction,bool show,int * node_counter,int * node_index)332 static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
333 // On OpenBSD the kernel thread 'swapper' has pid 0.
334 // Do not treat it as root of any tree.
335 if (pid == 0)
336 return;
337
338 Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
339
340 for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
341 Process* process = (Process*)Vector_get(this->processes, i);
342 if (process->show && Process_isChildOf(process, pid)) {
343 process = (Process*)Vector_take(this->processes, i);
344 Vector_add(children, process);
345 }
346 }
347
348 int size = Vector_size(children);
349 for (int i = 0; i < size; i++) {
350 int index = (*node_index)++;
351 Process* process = (Process*)Vector_get(children, i);
352
353 int lft = (*node_counter)++;
354
355 if (!show) {
356 process->show = false;
357 }
358
359 int s = Vector_size(this->processes2);
360 if (direction == 1) {
361 Vector_add(this->processes2, process);
362 } else {
363 Vector_insert(this->processes2, 0, process);
364 }
365
366 assert(Vector_size(this->processes2) == s + 1); (void)s;
367
368 int nextIndent = indent | (1 << level);
369 ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index);
370 if (i == size - 1) {
371 process->indent = -nextIndent;
372 } else {
373 process->indent = nextIndent;
374 }
375
376 int rht = (*node_counter)++;
377
378 process->tree_left = lft;
379 process->tree_right = rht;
380 process->tree_depth = level + 1;
381 process->tree_index = index;
382 Hashtable_put(this->displayTreeSet, index, process);
383 }
384 Vector_delete(children);
385 }
386
ProcessList_treeProcessCompare(const void * v1,const void * v2)387 static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
388 const Process* p1 = (const Process*)v1;
389 const Process* p2 = (const Process*)v2;
390
391 return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
392 }
393
ProcessList_treeProcessCompareByPID(const void * v1,const void * v2)394 static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
395 const Process* p1 = (const Process*)v1;
396 const Process* p2 = (const Process*)v2;
397
398 return SPACESHIP_NUMBER(p1->pid, p2->pid);
399 }
400
401 // Builds a sorted tree from scratch, without relying on previously gathered information
ProcessList_buildTree(ProcessList * this)402 static void ProcessList_buildTree(ProcessList* this) {
403 int node_counter = 1;
404 int node_index = 0;
405 int direction = Settings_getActiveDirection(this->settings);
406
407 // Sort by PID
408 Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
409 int vsize = Vector_size(this->processes);
410
411 // Find all processes whose parent is not visible
412 int size;
413 while ((size = Vector_size(this->processes))) {
414 int i;
415 for (i = 0; i < size; i++) {
416 Process* process = (Process*)Vector_get(this->processes, i);
417
418 // Immediately consume processes hidden from view
419 if (!process->show) {
420 process = (Process*)Vector_take(this->processes, i);
421 process->indent = 0;
422 process->tree_depth = 0;
423 process->tree_left = node_counter++;
424 process->tree_index = node_index++;
425 Vector_add(this->processes2, process);
426 ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
427 process->tree_right = node_counter++;
428 Hashtable_put(this->displayTreeSet, process->tree_index, process);
429 break;
430 }
431
432 pid_t ppid = Process_getParentPid(process);
433
434 // Bisect the process vector to find parent
435 int l = 0;
436 int r = size;
437
438 // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
439 // on Mac OS X 10.11.6) cancel bisecting and regard this process as
440 // root.
441 if (process->pid == ppid)
442 r = 0;
443
444 // On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
445 // use a ppid of 0. As that PID can't exist, we can skip searching for it.
446 if (!ppid)
447 r = 0;
448
449 while (l < r) {
450 int c = (l + r) / 2;
451 pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
452 if (ppid == pid) {
453 break;
454 } else if (ppid < pid) {
455 r = c;
456 } else {
457 l = c + 1;
458 }
459 }
460
461 // If parent not found, then construct the tree with this node as root
462 if (l >= r) {
463 process = (Process*)Vector_take(this->processes, i);
464 process->indent = 0;
465 process->tree_depth = 0;
466 process->tree_left = node_counter++;
467 process->tree_index = node_index++;
468 Vector_add(this->processes2, process);
469 Hashtable_put(this->displayTreeSet, process->tree_index, process);
470 ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index);
471 process->tree_right = node_counter++;
472 break;
473 }
474 }
475
476 // There should be no loop in the process tree
477 assert(i < size);
478 }
479
480 // Swap listings around
481 Vector* t = this->processes;
482 this->processes = this->processes2;
483 this->processes2 = t;
484
485 // Check consistency of the built structures
486 assert(Vector_size(this->processes) == vsize); (void)vsize;
487 assert(Vector_size(this->processes2) == 0);
488 }
489
ProcessList_sort(ProcessList * this)490 void ProcessList_sort(ProcessList* this) {
491 if (this->settings->treeView) {
492 ProcessList_updateTreeSet(this);
493 Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare);
494 } else {
495 Vector_insertionSort(this->processes);
496 }
497 }
498
ProcessList_keyAt(const ProcessList * this,int at)499 ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
500 int x = 0;
501 const ProcessField* fields = this->settings->fields;
502 ProcessField field;
503 for (int i = 0; (field = fields[i]); i++) {
504 int len = strlen(alignedProcessFieldTitle(this, field));
505 if (at >= x && at <= x + len) {
506 return field;
507 }
508 x += len;
509 }
510 return COMM;
511 }
512
ProcessList_expandTree(ProcessList * this)513 void ProcessList_expandTree(ProcessList* this) {
514 int size = Vector_size(this->processes);
515 for (int i = 0; i < size; i++) {
516 Process* process = (Process*) Vector_get(this->processes, i);
517 process->showChildren = true;
518 }
519 }
520
ProcessList_collapseAllBranches(ProcessList * this)521 void ProcessList_collapseAllBranches(ProcessList* this) {
522 int size = Vector_size(this->processes);
523 for (int i = 0; i < size; i++) {
524 Process* process = (Process*) Vector_get(this->processes, i);
525 // FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1
526 if (process->tree_depth > 0 && process->pid > 1)
527 process->showChildren = false;
528 }
529 }
530
ProcessList_rebuildPanel(ProcessList * this)531 void ProcessList_rebuildPanel(ProcessList* this) {
532 const char* incFilter = this->incFilter;
533
534 const int currPos = Panel_getSelectedIndex(this->panel);
535 const int currScrollV = this->panel->scrollV;
536 const int currSize = Panel_size(this->panel);
537
538 Panel_prune(this->panel);
539
540 /* Follow main process if followed a userland thread and threads are now hidden */
541 const Settings* settings = this->settings;
542 if (this->following != -1 && settings->hideUserlandThreads) {
543 const Process* followedProcess = (const Process*) Hashtable_get(this->processTable, this->following);
544 if (followedProcess && Process_isThread(followedProcess) && Hashtable_get(this->processTable, followedProcess->tgid) != NULL) {
545 this->following = followedProcess->tgid;
546 }
547 }
548
549 const int processCount = Vector_size(this->processes);
550 int idx = 0;
551 bool foundFollowed = false;
552
553 for (int i = 0; i < processCount; i++) {
554 Process* p = (Process*) Vector_get(this->processes, i);
555
556 if ( (!p->show)
557 || (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
558 || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
559 || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
560 continue;
561
562 Panel_set(this->panel, idx, (Object*)p);
563
564 if (this->following != -1 && p->pid == this->following) {
565 foundFollowed = true;
566 Panel_setSelected(this->panel, idx);
567 this->panel->scrollV = currScrollV;
568 }
569 idx++;
570 }
571
572 if (this->following != -1 && !foundFollowed) {
573 /* Reset if current followed pid not found */
574 this->following = -1;
575 Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
576 }
577
578 if (this->following == -1) {
579 /* If the last item was selected, keep the new last item selected */
580 if (currPos > 0 && currPos == currSize - 1)
581 Panel_setSelected(this->panel, Panel_size(this->panel) - 1);
582 else
583 Panel_setSelected(this->panel, currPos);
584
585 this->panel->scrollV = currScrollV;
586 }
587 }
588
ProcessList_getProcess(ProcessList * this,pid_t pid,bool * preExisting,Process_New constructor)589 Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
590 Process* proc = (Process*) Hashtable_get(this->processTable, pid);
591 *preExisting = proc != NULL;
592 if (proc) {
593 assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
594 assert(proc->pid == pid);
595 } else {
596 proc = constructor(this->settings);
597 assert(proc->cmdline == NULL);
598 proc->pid = pid;
599 }
600 return proc;
601 }
602
ProcessList_scan(ProcessList * this,bool pauseProcessUpdate)603 void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
604 // in pause mode only gather global data for meters (CPU/memory/...)
605 if (pauseProcessUpdate) {
606 ProcessList_goThroughEntries(this, true);
607 return;
608 }
609
610 // mark all process as "dirty"
611 for (int i = 0; i < Vector_size(this->processes); i++) {
612 Process* p = (Process*) Vector_get(this->processes, i);
613 p->updated = false;
614 p->wasShown = p->show;
615 p->show = true;
616 }
617
618 this->totalTasks = 0;
619 this->userlandThreads = 0;
620 this->kernelThreads = 0;
621 this->runningTasks = 0;
622
623
624 // set scan timestamp
625 static bool firstScanDone = false;
626 if (firstScanDone) {
627 Platform_gettime_monotonic(&this->monotonicMs);
628 } else {
629 this->monotonicMs = 0;
630 firstScanDone = true;
631 }
632
633 ProcessList_goThroughEntries(this, false);
634
635 uid_t maxUid = 0;
636 for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
637 Process* p = (Process*) Vector_get(this->processes, i);
638 Process_makeCommandStr(p);
639
640 // keep track of the highest UID for column scaling
641 if (p->st_uid > maxUid)
642 maxUid = p->st_uid;
643
644 if (p->tombStampMs > 0) {
645 // remove tombed process
646 if (this->monotonicMs >= p->tombStampMs) {
647 ProcessList_remove(this, p);
648 }
649 } else if (p->updated == false) {
650 // process no longer exists
651 if (this->settings->highlightChanges && p->wasShown) {
652 // mark tombed
653 p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
654 } else {
655 // immediately remove
656 ProcessList_remove(this, p);
657 }
658 }
659 }
660
661 // Set UID column width based on max UID.
662 Process_setUidColumnWidth(maxUid);
663
664 if (this->settings->treeView) {
665 // Clear out the hashtable to avoid any left-over processes from previous build
666 //
667 // The sorting algorithm relies on the fact that
668 // len(this->displayTreeSet) == len(this->processes)
669 Hashtable_clear(this->displayTreeSet);
670
671 ProcessList_buildTree(this);
672 }
673 }
674