2006-03-04 18:16:49 +00:00
|
|
|
/*
|
|
|
|
htop - ProcessList.c
|
|
|
|
(C) 2004,2005 Hisham H. Muhammad
|
2021-09-22 09:33:00 +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);
|
2021-12-16 16:09:03 +00:00
|
|
|
this->displayList = Vector_new(klass, false, DEFAULT_SIZE);
|
2020-12-01 22:27:04 +00:00
|
|
|
|
2020-11-29 13:35:23 +00:00
|
|
|
this->processTable = Hashtable_new(200, false);
|
2021-12-15 22:33:32 +00:00
|
|
|
this->needsSort = true;
|
2020-12-01 22:27:04 +00:00
|
|
|
|
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
|
|
|
|
2006-03-04 18:16:49 +00:00
|
|
|
Hashtable_delete(this->processTable);
|
2020-12-01 22:27:04 +00:00
|
|
|
|
2021-12-16 16:09:03 +00:00
|
|
|
Vector_delete(this->displayList);
|
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 "- ";
|
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
if (Process_fields[field].pidColumn) {
|
|
|
|
static char titleBuffer[PROCESS_MAX_PID_DIGITS + sizeof(" ")];
|
|
|
|
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
|
|
|
|
return titleBuffer;
|
|
|
|
}
|
2020-12-15 18:44:52 +00:00
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
if (field == ST_UID) {
|
|
|
|
static char titleBuffer[PROCESS_MAX_UID_DIGITS + sizeof(" ")];
|
|
|
|
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
|
|
|
|
return titleBuffer;
|
|
|
|
}
|
2020-12-15 18:44:52 +00:00
|
|
|
|
2021-12-04 18:57:47 +00:00
|
|
|
if (Process_fields[field].autoWidth) {
|
|
|
|
static char titleBuffer[UINT8_MAX + 1];
|
|
|
|
xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title);
|
|
|
|
return titleBuffer;
|
|
|
|
}
|
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
return title;
|
2020-12-15 18:44:52 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2021-08-31 05:38:52 +00:00
|
|
|
const ScreenSettings* ss = settings->ss;
|
|
|
|
const ProcessField* fields = ss->fields;
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2021-08-31 05:38:52 +00:00
|
|
|
ProcessField key = ScreenSettings_getActiveSortKey(ss);
|
2020-12-18 14:03:31 +00:00
|
|
|
|
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;
|
2021-08-31 05:38:52 +00:00
|
|
|
if (ss->treeView && ss->treeViewAlwaysByPID) {
|
2020-12-17 22:08:56 +00:00
|
|
|
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-08-31 05:38:52 +00:00
|
|
|
bool ascending = ScreenSettings_getActiveDirection(ss) == 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-08-31 05:38:52 +00:00
|
|
|
CRT_treeStr[ascending ? 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) {
|
2022-02-05 23:47:43 +00:00
|
|
|
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1);
|
2006-11-08 20:12:57 +00:00
|
|
|
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
|
|
|
|
2022-02-05 23:47:43 +00:00
|
|
|
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) != -1);
|
2006-11-08 20:12:57 +00:00
|
|
|
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
2022-02-05 23:47:43 +00:00
|
|
|
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2022-02-05 23:47:43 +00:00
|
|
|
// ProcessList_removeIndex removes Process p from the list's map and soft deletes
|
|
|
|
// it from its vector. Vector_compact *must* be called once the caller is done
|
|
|
|
// removing items.
|
2022-05-05 07:48:31 +00:00
|
|
|
// Should only be called from ProcessList_scan to avoid breaking dying process highlighting.
|
2022-02-05 23:47:43 +00:00
|
|
|
static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) {
|
2021-01-10 15:43:24 +00:00
|
|
|
pid_t pid = p->pid;
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2022-02-05 23:47:43 +00:00
|
|
|
assert(p == (Process*)Vector_get(this->processes, idx));
|
|
|
|
assert(Hashtable_get(this->processTable, pid) != NULL);
|
|
|
|
|
|
|
|
Hashtable_remove(this->processTable, pid);
|
|
|
|
Vector_softRemove(this->processes, idx);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
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);
|
2022-02-05 23:47:43 +00:00
|
|
|
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
|
|
|
|
}
|
|
|
|
|
2022-01-07 18:05:43 +00:00
|
|
|
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) {
|
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;
|
|
|
|
|
2021-12-16 15:36:01 +00:00
|
|
|
// The vector is sorted by parent PID, find the start of the range by bisection
|
|
|
|
int vsize = Vector_size(this->processes);
|
|
|
|
int l = 0;
|
|
|
|
int r = vsize;
|
|
|
|
while (l < r) {
|
|
|
|
int c = (l + r) / 2;
|
|
|
|
Process* process = (Process*)Vector_get(this->processes, c);
|
|
|
|
pid_t ppid = process->isRoot ? 0 : Process_getParentPid(process);
|
|
|
|
if (ppid < pid) {
|
|
|
|
l = c + 1;
|
|
|
|
} else {
|
|
|
|
r = c;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-16 15:36:01 +00:00
|
|
|
// Find the end to know the last line for indent handling purposes
|
|
|
|
int lastShown = r;
|
|
|
|
while (r < vsize) {
|
|
|
|
Process* process = (Process*)Vector_get(this->processes, r);
|
|
|
|
if (!Process_isChildOf(process, pid))
|
|
|
|
break;
|
|
|
|
if (process->show)
|
|
|
|
lastShown = r;
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = l; i < r; i++) {
|
|
|
|
Process* process = (Process*)Vector_get(this->processes, i);
|
2020-11-18 11:19:42 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-12-16 14:43:05 +00:00
|
|
|
Vector_add(this->displayList, process);
|
2020-11-01 00:09:51 +00:00
|
|
|
|
2010-11-22 12:40:20 +00:00
|
|
|
int nextIndent = indent | (1 << level);
|
2022-01-07 18:05:43 +00:00
|
|
|
ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < lastShown) ? nextIndent : indent, process->show && process->showChildren);
|
2021-12-16 15:48:47 +00:00
|
|
|
if (i == lastShown) {
|
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
|
|
|
|
2020-12-01 21:38:13 +00:00
|
|
|
process->tree_depth = level + 1;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-16 16:29:18 +00:00
|
|
|
static int compareProcessByKnownParentThenNatural(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
|
|
|
|
2021-12-16 15:36:01 +00:00
|
|
|
int result = SPACESHIP_NUMBER(
|
|
|
|
p1->isRoot ? 0 : Process_getParentPid(p1),
|
|
|
|
p2->isRoot ? 0 : Process_getParentPid(p2)
|
|
|
|
);
|
|
|
|
|
|
|
|
if (result != 0)
|
|
|
|
return result;
|
|
|
|
|
2021-12-16 16:29:18 +00:00
|
|
|
return Process_compare(v1, v2);
|
2020-11-18 11:19:42 +00:00
|
|
|
}
|
|
|
|
|
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) {
|
2021-12-16 16:09:03 +00:00
|
|
|
Vector_prune(this->displayList);
|
|
|
|
|
2021-12-16 15:36:01 +00:00
|
|
|
// Mark root processes
|
2020-11-18 11:19:42 +00:00
|
|
|
int vsize = Vector_size(this->processes);
|
2021-12-16 15:36:01 +00:00
|
|
|
for (int i = 0; i < vsize; i++) {
|
2021-12-16 16:09:03 +00:00
|
|
|
Process* process = (Process*)Vector_get(this->processes, i);
|
|
|
|
pid_t ppid = Process_getParentPid(process);
|
2021-12-16 15:36:01 +00:00
|
|
|
process->isRoot = false;
|
2021-12-16 16:09:03 +00:00
|
|
|
|
|
|
|
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
|
|
|
|
// on Mac OS X 10.11.6) regard this process as root.
|
2022-01-06 23:23:21 +00:00
|
|
|
if (process->pid == ppid) {
|
2021-12-16 15:36:01 +00:00
|
|
|
process->isRoot = true;
|
2022-01-06 23:23:21 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-12-16 16:09:03 +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.
|
2022-01-06 23:23:21 +00:00
|
|
|
if (!ppid) {
|
2021-12-16 15:36:01 +00:00
|
|
|
process->isRoot = true;
|
2022-01-06 23:23:21 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-12-16 16:09:03 +00:00
|
|
|
|
2021-12-16 15:36:01 +00:00
|
|
|
// We don't know about its parent for whatever reason
|
2021-12-16 16:09:03 +00:00
|
|
|
if (ProcessList_findProcess(this, ppid) == NULL)
|
2021-12-16 15:36:01 +00:00
|
|
|
process->isRoot = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort by known parent PID (roots first), then PID
|
2021-12-16 16:29:18 +00:00
|
|
|
Vector_quickSortCustomCompare(this->processes, compareProcessByKnownParentThenNatural);
|
2021-12-16 15:36:01 +00:00
|
|
|
|
|
|
|
// Find all processes whose parent is not visible
|
|
|
|
for (int i = 0; i < vsize; i++) {
|
|
|
|
Process* process = (Process*)Vector_get(this->processes, i);
|
2021-12-16 16:09:03 +00:00
|
|
|
|
|
|
|
// If parent not found, then construct the tree with this node as root
|
2021-12-16 15:36:01 +00:00
|
|
|
if (process->isRoot) {
|
2021-12-16 16:09:03 +00:00
|
|
|
process = (Process*)Vector_get(this->processes, i);
|
|
|
|
process->indent = 0;
|
|
|
|
process->tree_depth = 0;
|
|
|
|
Vector_add(this->displayList, process);
|
2022-01-07 18:05:43 +00:00
|
|
|
ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren);
|
2021-12-16 16:09:03 +00:00
|
|
|
continue;
|
|
|
|
}
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
2020-12-01 21:38:13 +00:00
|
|
|
|
2021-12-16 16:29:18 +00:00
|
|
|
this->needsSort = false;
|
|
|
|
|
2020-12-01 21:38:13 +00:00
|
|
|
// Check consistency of the built structures
|
2021-12-16 16:09:03 +00:00
|
|
|
assert(Vector_size(this->displayList) == vsize); (void)vsize;
|
2006-03-04 18:16:49 +00:00
|
|
|
}
|
|
|
|
|
2021-12-16 16:09:03 +00:00
|
|
|
void ProcessList_updateDisplayList(ProcessList* this) {
|
2021-08-31 05:38:52 +00:00
|
|
|
if (this->settings->ss->treeView) {
|
2021-12-16 16:29:18 +00:00
|
|
|
if (this->needsSort)
|
|
|
|
ProcessList_buildTree(this);
|
2020-12-02 17:52:09 +00:00
|
|
|
} else {
|
2021-12-16 16:09:03 +00:00
|
|
|
if (this->needsSort)
|
|
|
|
Vector_insertionSort(this->processes);
|
|
|
|
Vector_prune(this->displayList);
|
|
|
|
int size = Vector_size(this->processes);
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
Vector_add(this->displayList, Vector_get(this->processes, i));
|
2020-11-18 11:19:42 +00:00
|
|
|
}
|
2021-12-15 22:33:32 +00:00
|
|
|
this->needsSort = false;
|
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;
|
2021-08-31 05:38:52 +00:00
|
|
|
const ProcessField* fields = this->settings->ss->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
|
|
|
|
2022-05-20 20:57:36 +00:00
|
|
|
// Called on collapse-all toggle and on startup, possibly in non-tree mode
|
2021-02-12 17:48:09 +00:00
|
|
|
void ProcessList_collapseAllBranches(ProcessList* this) {
|
2022-05-20 20:57:36 +00:00
|
|
|
ProcessList_buildTree(this); // Update `tree_depth` fields of the processes
|
|
|
|
this->needsSort = true; // ProcessList is sorted by parent now, force new sort
|
2021-02-12 17:48:09 +00:00
|
|
|
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) {
|
2021-12-16 16:09:03 +00:00
|
|
|
ProcessList_updateDisplayList(this);
|
2021-12-15 22:33:32 +00:00
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
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-12-16 16:09:03 +00:00
|
|
|
const int processCount = Vector_size(this->displayList);
|
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-12-16 16:09:03 +00:00
|
|
|
Process* p = (Process*) Vector_get(this->displayList, 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))
|
2022-03-25 15:24:24 +00:00
|
|
|
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter, true)))
|
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) {
|
2022-02-05 23:47:43 +00:00
|
|
|
assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1);
|
2015-03-17 02:01:48 +00:00
|
|
|
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;
|
|
|
|
|
2021-12-04 18:57:47 +00:00
|
|
|
Process_resetFieldWidths();
|
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
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
uid_t maxUid = 0;
|
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-08-16 20:50:36 +00:00
|
|
|
// keep track of the highest UID for column scaling
|
|
|
|
if (p->st_uid > maxUid)
|
|
|
|
maxUid = p->st_uid;
|
|
|
|
|
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) {
|
2022-02-05 23:47:43 +00:00
|
|
|
ProcessList_removeIndex(this, p, i);
|
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
|
2022-02-05 23:47:43 +00:00
|
|
|
ProcessList_removeIndex(this, p, i);
|
2020-10-31 01:56:16 +00:00
|
|
|
}
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|
2020-11-18 11:19:42 +00:00
|
|
|
|
2022-02-05 23:47:43 +00:00
|
|
|
// Compact the processes vector in case of any deletions
|
|
|
|
Vector_compact(this->processes);
|
|
|
|
|
2021-08-16 20:50:36 +00:00
|
|
|
// Set UID column width based on max UID.
|
|
|
|
Process_setUidColumnWidth(maxUid);
|
2015-03-17 02:01:48 +00:00
|
|
|
}
|