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