2006-03-04 18:16:49 +00:00
|
|
|
/*
|
|
|
|
htop - ProcessList.c
|
|
|
|
(C) 2004,2005 Hisham H. Muhammad
|
2020-10-05 07:51:32 +00:00
|
|
|
Released under the GNU GPLv2, see the COPYING file
|
2006-03-04 18:16:49 +00:00
|
|
|
in the source distribution for its full text.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ProcessList.h"
|
2011-12-26 21:35:57 +00:00
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <assert.h>
|
2020-11-18 11:19:42 +00:00
|
|
|
#include <stdlib.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2011-12-26 21:35:57 +00:00
|
|
|
#include "CRT.h"
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
#include "DynamicColumn.h"
|
2020-11-18 11:19:42 +00:00
|
|
|
#include "Hashtable.h"
|
2020-12-06 14:22:41 +00:00
|
|
|
#include "Macros.h"
|
2021-03-30 04:55:48 +00:00
|
|
|
#include "Platform.h"
|
2020-11-18 11:19:42 +00:00
|
|
|
#include "Vector.h"
|
2020-10-14 18:21:09 +00:00
|
|
|
#include "XUtils.h"
|
2006-03-04 18:16:49 +00:00
|
|
|
|
|
|
|
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
|
2015-04-09 18:40:27 +00:00
|
|
|
this->processes = Vector_new(klass, true, DEFAULT_SIZE);
|
2020-12-01 22:27:04 +00:00
|
|
|
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
|
|
|
|
|
2020-11-29 13:35:23 +00:00
|
|
|
this->processTable = Hashtable_new(200, false);
|
2020-12-01 22:27:04 +00:00
|
|
|
this->displayTreeSet = Hashtable_new(200, false);
|
|
|
|
this->draftingTreeSet = Hashtable_new(200, false);
|
|
|
|
|
2006-03-04 18:16:49 +00:00
|
|
|
this->usersTable = usersTable;
|
2020-09-09 09:38:15 +00:00
|
|
|
this->pidMatchList = pidMatchList;
|
2021-06-23 07:44:56 +00:00
|
|
|
this->dynamicMeters = dynamicMeters;
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
this->dynamicColumns = dynamicColumns;
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2020-12-01 22:27:04 +00:00
|
|
|
this->userId = userId;
|
2020-11-18 11:19:42 +00:00
|
|
|
|
2014-11-24 20:55:03 +00:00
|
|
|
// set later by platform-specific code
|
2021-06-12 16:17:28 +00:00
|
|
|
this->activeCPUs = 0;
|
|
|
|
this->existingCPUs = 0;
|
2021-03-30 04:55:48 +00:00
|
|
|
this->monotonicMs = 0;
|
2020-10-31 01:56:16 +00:00
|
|
|
|
2021-04-07 23:26:48 +00:00
|
|
|
// always maintain valid realtime timestamps
|
|
|
|
Platform_gettime_realtime(&this->realtime, &this->realtimeMs);
|
|
|
|
|
2011-11-21 02:52:41 +00:00
|
|
|
#ifdef HAVE_LIBHWLOC
|
2011-09-24 00:30:47 +00:00
|
|
|
this->topologyOk = false;
|
2020-08-26 00:15:00 +00:00
|
|
|
if (hwloc_topology_init(&this->topology) == 0) {
|
|
|
|
this->topologyOk =
|
|
|
|
#if HWLOC_API_VERSION < 0x00020000
|
|
|
|
/* try to ignore the top-level machine object type */
|
|
|
|
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) &&
|
|
|
|
/* ignore caches, which don't add structure */
|
|
|
|
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) &&
|
|
|
|
0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) &&
|
|
|
|
0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) &&
|
|
|
|
#else
|
|
|
|
0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) &&
|
|
|
|
#endif
|
|
|
|
0 == hwloc_topology_load(this->topology);
|
2011-09-24 00:30:47 +00:00
|
|
|
}
|
|
|
|
#endif
|
2006-03-04 18:16:49 +00:00
|
|
|
|
2012-03-30 01:20:32 +00:00
|
|
|
this->following = -1;
|
2006-03-04 18:16:49 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-11-27 19:48:38 +00:00
|
|
|
void ProcessList_done(ProcessList* this) {
|
2016-03-01 00:57:27 +00:00
|
|
|
#ifdef HAVE_LIBHWLOC
|
|
|
|
if (this->topologyOk) {
|
|
|
|
hwloc_topology_destroy(this->topology);
|
|
|
|
}
|
|
|
|
#endif
|
2020-12-01 22:27:04 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
Hashtable_delete(this->draftingTreeSet);
|
2020-12-01 22:27:04 +00:00
|
|
|
Hashtable_delete(this->displayTreeSet);
|
2006-03-04 18:16:49 +00:00
|
|
|
Hashtable_delete(this->processTable);
|
2020-12-01 22:27:04 +00:00
|
|
|
|
2006-05-30 13:45:40 +00:00
|
|
|
Vector_delete(this->processes2);
|
2020-12-01 22:27:04 +00:00
|
|
|
Vector_delete(this->processes);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2011-12-01 12:31:57 +00:00
|
|
|
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
|
|
|
|
this->panel = panel;
|
|
|
|
}
|
|
|
|
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
|
|
|
|
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
|
|
|
|
if (column == NULL)
|
|
|
|
return "- ";
|
|
|
|
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
|
|
|
|
int width = column->width;
|
|
|
|
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
|
|
|
|
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
|
|
|
|
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
|
|
|
|
return titleBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
|
|
|
|
if (field >= LAST_PROCESSFIELD)
|
|
|
|
return alignedDynamicColumnTitle(this, field);
|
|
|
|
|
2020-12-15 18:44:52 +00:00
|
|
|
const char* title = Process_fields[field].title;
|
|
|
|
if (!title)
|
|
|
|
return "- ";
|
|
|
|
|
|
|
|
if (!Process_fields[field].pidColumn)
|
|
|
|
return title;
|
|
|
|
|
|
|
|
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
|
|
|
|
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
|
|
|
|
|
|
|
|
return titleBuffer;
|
|
|
|
}
|
|
|
|
|
2021-01-05 22:42:55 +00:00
|
|
|
void ProcessList_printHeader(const ProcessList* this, RichString* header) {
|
2021-03-07 14:31:41 +00:00
|
|
|
RichString_rewind(header, RichString_size(header));
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-12-17 22:08:56 +00:00
|
|
|
const Settings* settings = this->settings;
|
|
|
|
const ProcessField* fields = settings->fields;
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-12-18 14:03:31 +00:00
|
|
|
ProcessField key = Settings_getActiveSortKey(settings);
|
|
|
|
|
2006-03-04 18:16:49 +00:00
|
|
|
for (int i = 0; fields[i]; i++) {
|
2020-12-17 22:08:56 +00:00
|
|
|
int color;
|
|
|
|
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
|
|
|
color = CRT_colors[PANEL_HEADER_FOCUS];
|
2020-12-18 14:03:31 +00:00
|
|
|
} else if (key == fields[i]) {
|
2020-12-17 22:08:56 +00:00
|
|
|
color = CRT_colors[PANEL_SELECTION_FOCUS];
|
|
|
|
} else {
|
|
|
|
color = CRT_colors[PANEL_HEADER_FOCUS];
|
|
|
|
}
|
|
|
|
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i]));
|
2020-12-28 19:41:33 +00:00
|
|
|
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
|
2021-01-14 09:24:36 +00:00
|
|
|
RichString_rewind(header, 1); // rewind to override space
|
2020-12-28 19:41:33 +00:00
|
|
|
RichString_appendnWide(header,
|
|
|
|
CRT_colors[PANEL_SELECTION_FOCUS],
|
2021-01-21 13:27:23 +00:00
|
|
|
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC],
|
2020-12-28 19:41:33 +00:00
|
|
|
1);
|
|
|
|
}
|
2020-12-17 22:08:56 +00:00
|
|
|
if (COMM == fields[i] && settings->showMergedCommand) {
|
2020-12-04 13:44:57 +00:00
|
|
|
RichString_appendAscii(header, color, "(merged)");
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-24 20:55:03 +00:00
|
|
|
void ProcessList_add(ProcessList* this, Process* p) {
|
2006-11-08 20:12:57 +00:00
|
|
|
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
|
|
|
|
assert(Hashtable_get(this->processTable, p->pid) == NULL);
|
2020-10-31 01:56:16 +00:00
|
|
|
p->processList = this;
|
|
|
|
|
2020-11-16 11:17:28 +00:00
|
|
|
// highlighting processes found in first scan by first scan marked "far in the past"
|
2021-03-30 04:55:48 +00:00
|
|
|
p->seenStampMs = this->monotonicMs;
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2006-05-30 13:45:40 +00:00
|
|
|
Vector_add(this->processes, p);
|
2006-03-04 18:16:49 +00:00
|
|
|
Hashtable_put(this->processTable, p->pid, p);
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2006-11-08 20:12:57 +00:00
|
|
|
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
|
|
|
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
2006-11-12 21:53:56 +00:00
|
|
|
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2021-01-05 22:42:55 +00:00
|
|
|
void ProcessList_remove(ProcessList* this, const Process* p) {
|
2006-11-08 20:12:57 +00:00
|
|
|
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
|
|
|
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-01-05 22:42:55 +00:00
|
|
|
const Process* pp = Hashtable_remove(this->processTable, p->pid);
|
2006-11-08 21:49:52 +00:00
|
|
|
assert(pp == p); (void)pp;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2021-01-10 15:43:24 +00:00
|
|
|
pid_t pid = p->pid;
|
2010-02-25 01:37:31 +00:00
|
|
|
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
|
|
|
|
assert(idx != -1);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
|
|
|
if (idx >= 0) {
|
|
|
|
Vector_remove(this->processes, idx);
|
|
|
|
}
|
|
|
|
|
2021-01-10 15:43:24 +00:00
|
|
|
if (this->following != -1 && this->following == pid) {
|
|
|
|
this->following = -1;
|
|
|
|
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(Hashtable_get(this->processTable, pid) == NULL);
|
2006-11-12 21:53:56 +00:00
|
|
|
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
|
|
|
|
// relying only on itself.
|
|
|
|
//
|
|
|
|
// Algorithm
|
|
|
|
//
|
|
|
|
// The algorithm is based on `depth-first search`,
|
|
|
|
// even though `breadth-first search` approach may be more efficient on first glance,
|
2020-12-18 20:14:12 +00:00
|
|
|
// after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
|
2020-12-02 07:08:35 +00:00
|
|
|
// If it would be safe that approach would likely bring an advantage in performance.
|
2020-11-18 11:19:42 +00:00
|
|
|
//
|
|
|
|
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
|
|
|
|
// First it sorts a list. Then it runs the function recursively for each element of the sorted list.
|
|
|
|
// After that it updates the settings of processes.
|
|
|
|
//
|
|
|
|
// It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
|
|
|
|
//
|
2020-12-02 07:08:35 +00:00
|
|
|
// It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
|
2020-11-18 11:19:42 +00:00
|
|
|
// and at the same time make changes in it.
|
2020-12-02 07:08:35 +00:00
|
|
|
//
|
2020-11-18 11:19:42 +00:00
|
|
|
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) {
|
2020-12-02 07:08:35 +00:00
|
|
|
|
|
|
|
// It's guaranteed that layer_size is enough space
|
2020-11-18 11:19:42 +00:00
|
|
|
// but most likely it needs less. Specifically on first iteration.
|
|
|
|
int layerSize = (right - left) / 2;
|
|
|
|
|
2020-12-02 07:08:35 +00:00
|
|
|
// check if we reach `children` of `leaves`
|
2020-11-18 11:19:42 +00:00
|
|
|
if (layerSize == 0)
|
|
|
|
return;
|
|
|
|
|
2021-03-12 15:46:55 +00:00
|
|
|
Vector* layer = Vector_new(Vector_type(this->processes), false, layerSize);
|
2020-11-18 11:19:42 +00:00
|
|
|
|
|
|
|
// Find all processes on the same layer (process with the same `deep` value
|
2020-12-02 07:08:35 +00:00
|
|
|
// and included in a range from `leftBound` to `rightBound`).
|
2020-11-18 11:19:42 +00:00
|
|
|
//
|
|
|
|
// This loop also keeps track of left_bound and right_bound of these processes
|
|
|
|
// in order not to lose this information once the list is sorted.
|
|
|
|
//
|
2020-12-02 07:08:35 +00:00
|
|
|
// The variables left_bound and right_bound are different from what the values lhs and rhs represent.
|
2020-11-18 11:19:42 +00:00
|
|
|
// 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
|
|
|
|
//
|
|
|
|
// 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 …
|
|
|
|
//
|
|
|
|
// The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function.
|
|
|
|
//
|
|
|
|
// i | l | r
|
|
|
|
// 1 | 1 | 9
|
|
|
|
// 2 | 2 | 8
|
|
|
|
// 3 | 4 | 5
|
|
|
|
// 4 | 6 | 7
|
|
|
|
for (unsigned int i = leftBound; i < rightBound; i++) {
|
|
|
|
Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i);
|
2020-12-03 11:32:54 +00:00
|
|
|
assert(proc);
|
|
|
|
if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
|
2020-11-18 11:19:42 +00:00
|
|
|
if (Vector_size(layer) > 0) {
|
2020-12-01 21:38:13 +00:00
|
|
|
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
|
|
|
|
|
|
|
|
// Make a 'right_bound' of previous_process in a layer the current process's index.
|
2020-11-18 11:19:42 +00:00
|
|
|
//
|
|
|
|
// Use 'tree_depth' as a temporal variable.
|
2020-12-02 07:08:35 +00:00
|
|
|
// It's safe to do as later 'tree_depth' will be renovated.
|
2020-11-18 11:19:42 +00:00
|
|
|
previous_process->tree_depth = proc->tree_index;
|
|
|
|
}
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
Vector_add(layer, proc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-02 07:08:35 +00:00
|
|
|
// The loop above changes just up to process-1.
|
|
|
|
// So the last process of the layer isn't updated by the above code.
|
2020-11-18 11:19:42 +00:00
|
|
|
//
|
|
|
|
// Thus, if present, set the `rightBound` to the last process on the layer
|
|
|
|
if (Vector_size(layer) > 0) {
|
2020-12-01 21:38:13 +00:00
|
|
|
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
|
2020-11-18 11:19:42 +00:00
|
|
|
previous_process->tree_depth = rightBound;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector_quickSort(layer);
|
|
|
|
|
|
|
|
int size = Vector_size(layer);
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
Process* proc = (Process*)Vector_get(layer, i);
|
|
|
|
|
|
|
|
unsigned int idx = (*index)++;
|
|
|
|
int newLeft = (*treeIndex)++;
|
|
|
|
|
2020-12-01 21:38:13 +00:00
|
|
|
int level = deep == 0 ? 0 : (int)deep - 1;
|
2020-11-18 11:19:42 +00:00
|
|
|
int currentIndent = indent == -1 ? 0 : indent | (1 << level);
|
2020-12-01 21:38:13 +00:00
|
|
|
int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent);
|
2020-11-18 11:19:42 +00:00
|
|
|
|
|
|
|
unsigned int newLeftBound = proc->tree_index;
|
|
|
|
unsigned int newRightBound = proc->tree_depth;
|
2020-12-01 21:38:13 +00:00
|
|
|
ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent);
|
2020-11-18 11:19:42 +00:00
|
|
|
|
|
|
|
int newRight = (*treeIndex)++;
|
|
|
|
|
|
|
|
proc->tree_left = newLeft;
|
|
|
|
proc->tree_right = newRight;
|
|
|
|
proc->tree_index = idx;
|
|
|
|
proc->tree_depth = deep;
|
|
|
|
|
|
|
|
if (indent == -1) {
|
|
|
|
proc->indent = 0;
|
|
|
|
} else if (i == size - 1) {
|
|
|
|
proc->indent = -currentIndent;
|
|
|
|
} else {
|
|
|
|
proc->indent = currentIndent;
|
|
|
|
}
|
|
|
|
|
|
|
|
Hashtable_put(this->draftingTreeSet, proc->tree_index, proc);
|
2020-11-22 18:36:07 +00:00
|
|
|
|
|
|
|
// It's not strictly necessary to do this, but doing so anyways
|
|
|
|
// allows for checking the correctness of the inner workings.
|
|
|
|
Hashtable_remove(this->displayTreeSet, newLeftBound);
|
2020-11-18 11:19:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector_delete(layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ProcessList_updateTreeSet(ProcessList* this) {
|
|
|
|
unsigned int index = 0;
|
|
|
|
unsigned int tree_index = 1;
|
|
|
|
|
2020-11-22 18:36:07 +00:00
|
|
|
const int vsize = Vector_size(this->processes);
|
2020-11-18 11:19:42 +00:00
|
|
|
|
2020-11-22 18:36:07 +00:00
|
|
|
assert(Hashtable_count(this->draftingTreeSet) == 0);
|
|
|
|
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
|
|
|
|
|
|
|
|
ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1);
|
|
|
|
|
|
|
|
Hashtable* tmp = this->draftingTreeSet;
|
|
|
|
this->draftingTreeSet = this->displayTreeSet;
|
|
|
|
this->displayTreeSet = tmp;
|
|
|
|
|
|
|
|
assert(Hashtable_count(this->draftingTreeSet) == 0);
|
|
|
|
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
|
2020-11-18 11:19:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
|
2021-03-19 18:54:29 +00:00
|
|
|
// On OpenBSD the kernel thread 'swapper' has pid 0.
|
|
|
|
// Do not treat it as root of any tree.
|
|
|
|
if (pid == 0)
|
|
|
|
return;
|
|
|
|
|
2012-12-05 15:12:20 +00:00
|
|
|
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
|
2006-03-04 18:16:49 +00:00
|
|
|
|
2006-11-08 20:12:57 +00:00
|
|
|
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
2020-12-01 21:38:13 +00:00
|
|
|
Process* process = (Process*)Vector_get(this->processes, i);
|
2017-09-14 20:10:39 +00:00
|
|
|
if (process->show && Process_isChildOf(process, pid)) {
|
2020-12-01 21:38:13 +00:00
|
|
|
process = (Process*)Vector_take(this->processes, i);
|
2006-05-30 13:45:40 +00:00
|
|
|
Vector_add(children, process);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-18 11:19:42 +00:00
|
|
|
|
2006-05-30 13:45:40 +00:00
|
|
|
int size = Vector_size(children);
|
2006-03-04 18:16:49 +00:00
|
|
|
for (int i = 0; i < size; i++) {
|
2020-11-18 11:19:42 +00:00
|
|
|
int index = (*node_index)++;
|
2020-12-01 21:38:13 +00:00
|
|
|
Process* process = (Process*)Vector_get(children, i);
|
2020-11-18 11:19:42 +00:00
|
|
|
|
|
|
|
int lft = (*node_counter)++;
|
|
|
|
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!show) {
|
2010-11-22 12:40:20 +00:00
|
|
|
process->show = false;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
2020-10-15 19:45:38 +00:00
|
|
|
int s = Vector_size(this->processes2);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (direction == 1) {
|
2010-11-22 12:40:20 +00:00
|
|
|
Vector_add(this->processes2, process);
|
2020-11-01 00:09:51 +00:00
|
|
|
} else {
|
2010-11-22 12:40:20 +00:00
|
|
|
Vector_insert(this->processes2, 0, process);
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
2020-12-01 21:38:13 +00:00
|
|
|
assert(Vector_size(this->processes2) == s + 1); (void)s;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2010-11-22 12:40:20 +00:00
|
|
|
int nextIndent = indent | (1 << level);
|
2020-12-01 21:38:13 +00:00
|
|
|
ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (i == size - 1) {
|
2011-11-03 22:12:12 +00:00
|
|
|
process->indent = -nextIndent;
|
2020-11-01 00:09:51 +00:00
|
|
|
} else {
|
2011-11-03 22:12:12 +00:00
|
|
|
process->indent = nextIndent;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-11-18 11:19:42 +00:00
|
|
|
|
|
|
|
int rht = (*node_counter)++;
|
|
|
|
|
|
|
|
process->tree_left = lft;
|
|
|
|
process->tree_right = rht;
|
2020-12-01 21:38:13 +00:00
|
|
|
process->tree_depth = level + 1;
|
2020-11-18 11:19:42 +00:00
|
|
|
process->tree_index = index;
|
|
|
|
Hashtable_put(this->displayTreeSet, index, process);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2006-05-30 13:45:40 +00:00
|
|
|
Vector_delete(children);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 12:02:32 +00:00
|
|
|
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
2021-07-14 17:24:18 +00:00
|
|
|
const Process* p1 = (const Process*)v1;
|
|
|
|
const Process* p2 = (const Process*)v2;
|
2020-10-21 19:26:09 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
2020-10-21 19:26:09 +00:00
|
|
|
}
|
|
|
|
|
2020-12-23 12:02:32 +00:00
|
|
|
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
2021-07-14 17:24:18 +00:00
|
|
|
const Process* p1 = (const Process*)v1;
|
|
|
|
const Process* p2 = (const Process*)v2;
|
2020-11-18 11:19:42 +00:00
|
|
|
|
|
|
|
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
|
|
|
}
|
|
|
|
|
2020-12-01 21:38:13 +00:00
|
|
|
// Builds a sorted tree from scratch, without relying on previously gathered information
|
2020-11-18 11:19:42 +00:00
|
|
|
static void ProcessList_buildTree(ProcessList* this) {
|
|
|
|
int node_counter = 1;
|
|
|
|
int node_index = 0;
|
2020-12-18 14:03:31 +00:00
|
|
|
int direction = Settings_getActiveDirection(this->settings);
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// Sort by PID
|
|
|
|
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
|
|
|
|
int vsize = Vector_size(this->processes);
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// Find all processes whose parent is not visible
|
|
|
|
int size;
|
|
|
|
while ((size = Vector_size(this->processes))) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++) {
|
2020-12-01 21:38:13 +00:00
|
|
|
Process* process = (Process*)Vector_get(this->processes, i);
|
|
|
|
|
|
|
|
// Immediately consume processes hidden from view
|
2020-11-18 11:19:42 +00:00
|
|
|
if (!process->show) {
|
2020-12-01 21:38:13 +00:00
|
|
|
process = (Process*)Vector_take(this->processes, i);
|
2020-11-18 11:19:42 +00:00
|
|
|
process->indent = 0;
|
|
|
|
process->tree_depth = 0;
|
2020-12-01 21:38:13 +00:00
|
|
|
process->tree_left = node_counter++;
|
|
|
|
process->tree_index = node_index++;
|
2020-11-18 11:19:42 +00:00
|
|
|
Vector_add(this->processes2, process);
|
|
|
|
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
|
2020-12-01 21:38:13 +00:00
|
|
|
process->tree_right = node_counter++;
|
2020-11-18 11:19:42 +00:00
|
|
|
Hashtable_put(this->displayTreeSet, process->tree_index, process);
|
|
|
|
break;
|
|
|
|
}
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
pid_t ppid = Process_getParentPid(process);
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// Bisect the process vector to find parent
|
2020-12-01 21:38:13 +00:00
|
|
|
int l = 0;
|
|
|
|
int r = size;
|
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
|
|
|
|
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
|
|
|
|
// root.
|
|
|
|
if (process->pid == ppid)
|
|
|
|
r = 0;
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-12-01 22:34:06 +00:00
|
|
|
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
|
|
|
|
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
|
|
|
|
if (!ppid)
|
|
|
|
r = 0;
|
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
while (l < r) {
|
|
|
|
int c = (l + r) / 2;
|
2020-12-01 21:38:13 +00:00
|
|
|
pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
|
2020-11-18 11:19:42 +00:00
|
|
|
if (ppid == pid) {
|
2017-09-01 13:27:24 +00:00
|
|
|
break;
|
2020-11-18 11:19:42 +00:00
|
|
|
} else if (ppid < pid) {
|
|
|
|
r = c;
|
|
|
|
} else {
|
|
|
|
l = c + 1;
|
2017-09-01 13:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-01 21:38:13 +00:00
|
|
|
|
|
|
|
// If parent not found, then construct the tree with this node as root
|
2020-11-18 11:19:42 +00:00
|
|
|
if (l >= r) {
|
2020-12-01 21:38:13 +00:00
|
|
|
process = (Process*)Vector_take(this->processes, i);
|
2020-11-18 11:19:42 +00:00
|
|
|
process->indent = 0;
|
|
|
|
process->tree_depth = 0;
|
2020-12-01 21:38:13 +00:00
|
|
|
process->tree_left = node_counter++;
|
|
|
|
process->tree_index = node_index++;
|
2020-11-18 11:19:42 +00:00
|
|
|
Vector_add(this->processes2, process);
|
|
|
|
Hashtable_put(this->displayTreeSet, process->tree_index, process);
|
|
|
|
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index);
|
2020-12-01 21:38:13 +00:00
|
|
|
process->tree_right = node_counter++;
|
2020-11-18 11:19:42 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-11-12 21:53:56 +00:00
|
|
|
}
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// There should be no loop in the process tree
|
|
|
|
assert(i < size);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
// Swap listings around
|
|
|
|
Vector* t = this->processes;
|
|
|
|
this->processes = this->processes2;
|
|
|
|
this->processes2 = t;
|
2020-12-01 21:38:13 +00:00
|
|
|
|
|
|
|
// Check consistency of the built structures
|
|
|
|
assert(Vector_size(this->processes) == vsize); (void)vsize;
|
|
|
|
assert(Vector_size(this->processes2) == 0);
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2020-11-18 11:19:42 +00:00
|
|
|
void ProcessList_sort(ProcessList* this) {
|
2020-12-02 17:52:09 +00:00
|
|
|
if (this->settings->treeView) {
|
2020-11-18 11:19:42 +00:00
|
|
|
ProcessList_updateTreeSet(this);
|
|
|
|
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare);
|
2020-12-02 17:52:09 +00:00
|
|
|
} else {
|
|
|
|
Vector_insertionSort(this->processes);
|
2020-11-18 11:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
2008-03-14 18:50:49 +00:00
|
|
|
|
2020-11-04 16:46:11 +00:00
|
|
|
ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
|
2008-03-14 18:50:49 +00:00
|
|
|
int x = 0;
|
2020-10-21 19:26:09 +00:00
|
|
|
const ProcessField* fields = this->settings->fields;
|
2008-03-14 18:50:49 +00:00
|
|
|
ProcessField field;
|
|
|
|
for (int i = 0; (field = fields[i]); i++) {
|
PCP: support for 'dynamic columns' added at runtime
Implements support for arbitrary Performance Co-Pilot
metrics with per-process instance domains to form new
htop columns. The column-to-metric mappings are setup
using configuration files which will be documented via
man pages as part of a follow-up commit.
We provide an initial set of column configurations so
as to provide new capabilities to pcp-htop: including
configs for containers, open fd counts, scheduler run
queue time, tcp/udp bytes/calls sent/recv, delay acct,
virtual machine guests, detailed virtual memory, swap.
Note there is a change to the configuration file path
resolution algorithm introduced for 'dynamic meters'.
First, look in any custom PCP_HTOP_DIR location. Then
iterate, in priority order, users home directory, then
local sysadmins files in /etc/pcp/htop, then readonly
configuration files below /usr/share/pcp/htop. This
final location becomes the preferred place for our own
shipped meter and column files.
The Settings file (htoprc) writing code is updated to
not using the numeric identifier for dynamic columns.
The same strategy used for dynamic meters is used here
where we write Dynamic(name) so the name can be setup
once more at start. Regular (static) columns writing
to htoprc - i.e. numerically indexed - is unchanged.
2021-07-11 01:11:29 +00:00
|
|
|
int len = strlen(alignedProcessFieldTitle(this, field));
|
2008-03-14 18:50:49 +00:00
|
|
|
if (at >= x && at <= x + len) {
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
x += len;
|
|
|
|
}
|
|
|
|
return COMM;
|
|
|
|
}
|
2010-08-24 23:20:38 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2011-12-01 12:31:57 +00:00
|
|
|
|
2021-02-12 17:48:09 +00:00
|
|
|
void ProcessList_collapseAllBranches(ProcessList* this) {
|
|
|
|
int size = Vector_size(this->processes);
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
Process* process = (Process*) Vector_get(this->processes, i);
|
|
|
|
// FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1
|
|
|
|
if (process->tree_depth > 0 && process->pid > 1)
|
|
|
|
process->showChildren = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
void ProcessList_rebuildPanel(ProcessList* this) {
|
|
|
|
const char* incFilter = this->incFilter;
|
2011-12-01 12:31:57 +00:00
|
|
|
|
2021-03-12 17:02:39 +00:00
|
|
|
const int currPos = Panel_getSelectedIndex(this->panel);
|
|
|
|
const int currScrollV = this->panel->scrollV;
|
|
|
|
const int currSize = Panel_size(this->panel);
|
2011-12-01 12:31:57 +00:00
|
|
|
|
|
|
|
Panel_prune(this->panel);
|
2021-03-12 17:02:39 +00:00
|
|
|
|
|
|
|
/* Follow main process if followed a userland thread and threads are now hidden */
|
|
|
|
const Settings* settings = this->settings;
|
|
|
|
if (this->following != -1 && settings->hideUserlandThreads) {
|
|
|
|
const Process* followedProcess = (const Process*) Hashtable_get(this->processTable, this->following);
|
|
|
|
if (followedProcess && Process_isThread(followedProcess) && Hashtable_get(this->processTable, followedProcess->tgid) != NULL) {
|
|
|
|
this->following = followedProcess->tgid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 13:50:56 +00:00
|
|
|
const int processCount = Vector_size(this->processes);
|
2011-12-01 12:31:57 +00:00
|
|
|
int idx = 0;
|
2021-03-03 18:56:39 +00:00
|
|
|
bool foundFollowed = false;
|
2021-01-30 22:45:00 +00:00
|
|
|
|
2021-03-12 17:02:39 +00:00
|
|
|
for (int i = 0; i < processCount; i++) {
|
2021-04-18 13:50:56 +00:00
|
|
|
Process* p = (Process*) Vector_get(this->processes, i);
|
2011-12-01 12:31:57 +00:00
|
|
|
|
|
|
|
if ( (!p->show)
|
2015-01-22 01:27:31 +00:00
|
|
|
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
2020-10-17 10:54:45 +00:00
|
|
|
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|
2020-09-09 09:38:15 +00:00
|
|
|
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
|
2020-12-08 20:24:19 +00:00
|
|
|
continue;
|
2011-12-01 12:31:57 +00:00
|
|
|
|
2020-12-08 20:24:19 +00:00
|
|
|
Panel_set(this->panel, idx, (Object*)p);
|
2021-01-30 22:45:00 +00:00
|
|
|
|
|
|
|
if (this->following != -1 && p->pid == this->following) {
|
2021-03-03 18:56:39 +00:00
|
|
|
foundFollowed = true;
|
2020-12-08 20:24:19 +00:00
|
|
|
Panel_setSelected(this->panel, idx);
|
|
|
|
this->panel->scrollV = currScrollV;
|
2011-12-01 12:31:57 +00:00
|
|
|
}
|
2020-12-08 20:24:19 +00:00
|
|
|
idx++;
|
2011-12-01 12:31:57 +00:00
|
|
|
}
|
2021-01-30 22:45:00 +00:00
|
|
|
|
2021-03-03 18:56:39 +00:00
|
|
|
if (this->following != -1 && !foundFollowed) {
|
|
|
|
/* Reset if current followed pid not found */
|
|
|
|
this->following = -1;
|
|
|
|
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
|
|
|
}
|
|
|
|
|
2021-01-30 22:45:00 +00:00
|
|
|
if (this->following == -1) {
|
|
|
|
/* If the last item was selected, keep the new last item selected */
|
2021-02-21 18:06:02 +00:00
|
|
|
if (currPos > 0 && currPos == currSize - 1)
|
2021-01-30 22:45:00 +00:00
|
|
|
Panel_setSelected(this->panel, Panel_size(this->panel) - 1);
|
|
|
|
else
|
|
|
|
Panel_setSelected(this->panel, currPos);
|
|
|
|
|
|
|
|
this->panel->scrollV = currScrollV;
|
|
|
|
}
|
2011-12-01 12:31:57 +00:00
|
|
|
}
|
2014-11-24 20:55:03 +00:00
|
|
|
|
2015-04-02 04:57:37 +00:00
|
|
|
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
|
2015-03-17 02:01:48 +00:00
|
|
|
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
|
2021-04-18 13:51:46 +00:00
|
|
|
*preExisting = proc != NULL;
|
2015-03-17 02:01:48 +00:00
|
|
|
if (proc) {
|
|
|
|
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
|
|
|
|
assert(proc->pid == pid);
|
|
|
|
} else {
|
|
|
|
proc = constructor(this->settings);
|
2020-12-19 15:21:08 +00:00
|
|
|
assert(proc->cmdline == NULL);
|
2015-03-17 02:01:48 +00:00
|
|
|
proc->pid = pid;
|
|
|
|
}
|
|
|
|
return proc;
|
|
|
|
}
|
|
|
|
|
2020-10-13 14:03:37 +00:00
|
|
|
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
|
|
|
// in pause mode only gather global data for meters (CPU/memory/...)
|
|
|
|
if (pauseProcessUpdate) {
|
|
|
|
ProcessList_goThroughEntries(this, true);
|
|
|
|
return;
|
|
|
|
}
|
2015-03-17 02:01:48 +00:00
|
|
|
|
|
|
|
// mark all process as "dirty"
|
|
|
|
for (int i = 0; i < Vector_size(this->processes); i++) {
|
|
|
|
Process* p = (Process*) Vector_get(this->processes, i);
|
|
|
|
p->updated = false;
|
2020-11-01 00:36:53 +00:00
|
|
|
p->wasShown = p->show;
|
2016-02-18 16:32:49 +00:00
|
|
|
p->show = true;
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this->totalTasks = 0;
|
|
|
|
this->userlandThreads = 0;
|
|
|
|
this->kernelThreads = 0;
|
|
|
|
this->runningTasks = 0;
|
|
|
|
|
2020-10-31 01:56:16 +00:00
|
|
|
|
2021-03-30 04:55:48 +00:00
|
|
|
// set scan timestamp
|
2020-11-16 11:17:28 +00:00
|
|
|
static bool firstScanDone = false;
|
2021-03-30 04:55:48 +00:00
|
|
|
if (firstScanDone) {
|
|
|
|
Platform_gettime_monotonic(&this->monotonicMs);
|
|
|
|
} else {
|
|
|
|
this->monotonicMs = 0;
|
2020-11-16 11:17:28 +00:00
|
|
|
firstScanDone = true;
|
2020-10-31 01:56:16 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 14:03:37 +00:00
|
|
|
ProcessList_goThroughEntries(this, false);
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2015-03-17 02:01:48 +00:00
|
|
|
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
|
|
|
Process* p = (Process*) Vector_get(this->processes, i);
|
2021-04-18 16:10:04 +00:00
|
|
|
Process_makeCommandStr(p);
|
|
|
|
|
2021-03-30 04:55:48 +00:00
|
|
|
if (p->tombStampMs > 0) {
|
2020-10-31 01:56:16 +00:00
|
|
|
// remove tombed process
|
2021-03-30 04:55:48 +00:00
|
|
|
if (this->monotonicMs >= p->tombStampMs) {
|
2020-11-20 16:50:30 +00:00
|
|
|
ProcessList_remove(this, p);
|
2020-10-31 01:56:16 +00:00
|
|
|
}
|
|
|
|
} else if (p->updated == false) {
|
|
|
|
// process no longer exists
|
2020-11-01 00:36:53 +00:00
|
|
|
if (this->settings->highlightChanges && p->wasShown) {
|
2020-10-31 01:56:16 +00:00
|
|
|
// mark tombed
|
2021-03-30 04:55:48 +00:00
|
|
|
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
|
2020-10-31 01:56:16 +00:00
|
|
|
} else {
|
|
|
|
// immediately remove
|
|
|
|
ProcessList_remove(this, p);
|
|
|
|
}
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|
2020-11-18 11:19:42 +00:00
|
|
|
|
2020-12-01 23:16:49 +00:00
|
|
|
if (this->settings->treeView) {
|
|
|
|
// Clear out the hashtable to avoid any left-over processes from previous build
|
|
|
|
//
|
|
|
|
// The sorting algorithm relies on the fact that
|
|
|
|
// len(this->displayTreeSet) == len(this->processes)
|
|
|
|
Hashtable_clear(this->displayTreeSet);
|
|
|
|
|
|
|
|
ProcessList_buildTree(this);
|
|
|
|
}
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|