2017-04-19 14:12:17 +00:00
|
|
|
/*
|
|
|
|
htop - DragonFlyBSDProcessList.c
|
|
|
|
(C) 2014 Hisham H. Muhammad
|
|
|
|
(C) 2017 Diederik de Groot
|
2021-09-22 09:33:00 +00:00
|
|
|
Released under the GNU GPLv2+, see the COPYING file
|
2017-04-19 14:12:17 +00:00
|
|
|
in the source distribution for its full text.
|
|
|
|
*/
|
|
|
|
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "dragonflybsd/DragonFlyBSDProcessList.h"
|
2017-04-19 14:12:17 +00:00
|
|
|
|
2021-05-22 07:24:30 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stddef.h>
|
2017-04-19 14:12:17 +00:00
|
|
|
#include <stdlib.h>
|
2021-05-22 07:24:30 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2017-04-19 14:12:17 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/user.h>
|
2017-04-20 13:14:33 +00:00
|
|
|
#include <sys/param.h>
|
2017-04-19 14:12:17 +00:00
|
|
|
|
2020-10-15 20:37:02 +00:00
|
|
|
#include "CRT.h"
|
|
|
|
#include "Macros.h"
|
2021-05-22 07:24:30 +00:00
|
|
|
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "dragonflybsd/DragonFlyBSDProcess.h"
|
2020-10-15 20:37:02 +00:00
|
|
|
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
static int MIB_hw_physmem[2];
|
|
|
|
static int MIB_vm_stats_vm_v_page_count[4];
|
|
|
|
static int pageSize;
|
|
|
|
static int pageSizeKb;
|
|
|
|
|
|
|
|
static int MIB_vm_stats_vm_v_wire_count[4];
|
|
|
|
static int MIB_vm_stats_vm_v_active_count[4];
|
|
|
|
static int MIB_vm_stats_vm_v_cache_count[4];
|
|
|
|
static int MIB_vm_stats_vm_v_inactive_count[4];
|
|
|
|
static int MIB_vm_stats_vm_v_free_count[4];
|
|
|
|
|
|
|
|
static int MIB_vfs_bufspace[2];
|
|
|
|
|
|
|
|
static int MIB_kern_cp_time[2];
|
|
|
|
static int MIB_kern_cp_times[2];
|
|
|
|
static int kernelFScale;
|
|
|
|
|
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_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
|
2017-04-19 14:12:17 +00:00
|
|
|
size_t len;
|
|
|
|
char errbuf[_POSIX2_LINE_MAX];
|
|
|
|
DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList));
|
|
|
|
ProcessList* pl = (ProcessList*) dfpl;
|
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_init(pl, Class(DragonFlyBSDProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId);
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
// physical memory in system: hw.physmem
|
|
|
|
// physical page size: hw.pagesize
|
|
|
|
// usable pagesize : vm.stats.vm.v_page_size
|
|
|
|
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
|
|
|
|
|
|
|
|
len = sizeof(pageSize);
|
2020-12-10 00:57:48 +00:00
|
|
|
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
|
|
|
|
CRT_fatalError("Cannot get pagesize by sysctl");
|
|
|
|
pageSizeKb = pageSize / ONE_K;
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
// usable page count vm.stats.vm.v_page_count
|
|
|
|
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
|
|
|
|
len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len);
|
|
|
|
|
|
|
|
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len);
|
|
|
|
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len);
|
|
|
|
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len);
|
|
|
|
len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len);
|
|
|
|
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len);
|
|
|
|
|
|
|
|
len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len);
|
|
|
|
|
|
|
|
int cpus = 1;
|
|
|
|
len = sizeof(cpus);
|
|
|
|
if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) != 0) {
|
|
|
|
cpus = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
|
|
|
|
len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len);
|
|
|
|
dfpl->cp_time_o = xCalloc(cpus, sizeof_cp_time_array);
|
|
|
|
dfpl->cp_time_n = xCalloc(cpus, sizeof_cp_time_array);
|
|
|
|
len = sizeof_cp_time_array;
|
|
|
|
|
|
|
|
// fetch initial single (or average) CPU clicks from kernel
|
|
|
|
sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_o, &len, NULL, 0);
|
|
|
|
|
|
|
|
// on smp box, fetch rest of initial CPU's clicks
|
|
|
|
if (cpus > 1) {
|
|
|
|
len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len);
|
|
|
|
dfpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array);
|
|
|
|
dfpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array);
|
|
|
|
len = cpus * sizeof_cp_time_array;
|
|
|
|
sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
pl->existingCPUs = MAXIMUM(cpus, 1);
|
|
|
|
// TODO: support offline CPUs and hot swapping
|
|
|
|
pl->activeCPUs = pl->existingCPUs;
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
if (cpus == 1 ) {
|
2020-10-31 21:14:27 +00:00
|
|
|
dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData));
|
2017-04-19 14:12:17 +00:00
|
|
|
} else {
|
2020-10-31 21:14:27 +00:00
|
|
|
// on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
|
2021-06-12 16:17:28 +00:00
|
|
|
dfpl->cpus = xRealloc(dfpl->cpus, (pl->existingCPUs + 1) * sizeof(CPUData));
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
len = sizeof(kernelFScale);
|
|
|
|
if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) {
|
|
|
|
//sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed
|
|
|
|
kernelFScale = 2048;
|
|
|
|
}
|
|
|
|
|
|
|
|
dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
|
|
|
|
if (dfpl->kd == NULL) {
|
2021-01-07 15:08:43 +00:00
|
|
|
CRT_fatalError("kvm_openfiles() failed");
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return pl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessList_delete(ProcessList* this) {
|
|
|
|
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
|
2020-11-01 00:09:51 +00:00
|
|
|
if (dfpl->kd) {
|
|
|
|
kvm_close(dfpl->kd);
|
|
|
|
}
|
2017-04-19 14:12:17 +00:00
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
if (dfpl->jails) {
|
|
|
|
Hashtable_delete(dfpl->jails);
|
|
|
|
}
|
2017-04-19 14:12:17 +00:00
|
|
|
free(dfpl->cp_time_o);
|
|
|
|
free(dfpl->cp_time_n);
|
|
|
|
free(dfpl->cp_times_o);
|
|
|
|
free(dfpl->cp_times_n);
|
|
|
|
free(dfpl->cpus);
|
|
|
|
|
|
|
|
ProcessList_done(this);
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
|
|
|
|
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl;
|
|
|
|
|
2021-06-12 16:17:28 +00:00
|
|
|
unsigned int cpus = pl->existingCPUs; // actual CPU count
|
|
|
|
unsigned int maxcpu = cpus; // max iteration (in case we have average + smp)
|
2017-04-19 14:12:17 +00:00
|
|
|
int cp_times_offset;
|
|
|
|
|
|
|
|
assert(cpus > 0);
|
|
|
|
|
|
|
|
size_t sizeof_cp_time_array;
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
unsigned long* cp_time_n; // old clicks state
|
|
|
|
unsigned long* cp_time_o; // current clicks state
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
unsigned long cp_time_d[CPUSTATES];
|
|
|
|
double cp_time_p[CPUSTATES];
|
|
|
|
|
|
|
|
// get averages or single CPU clicks
|
|
|
|
sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
|
|
|
|
sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_n, &sizeof_cp_time_array, NULL, 0);
|
|
|
|
|
|
|
|
// get rest of CPUs
|
|
|
|
if (cpus > 1) {
|
2020-10-31 21:14:27 +00:00
|
|
|
// on smp systems DragonFlyBSD kernel concats all CPU states into one long array in
|
|
|
|
// kern.cp_times sysctl OID
|
|
|
|
// we store averages in dfpl->cpus[0], and actual cores after that
|
|
|
|
maxcpu = cpus + 1;
|
|
|
|
sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
|
|
|
|
sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
2021-02-17 16:38:35 +00:00
|
|
|
for (unsigned int i = 0; i < maxcpu; i++) {
|
2017-04-19 14:12:17 +00:00
|
|
|
if (cpus == 1) {
|
|
|
|
// single CPU box
|
|
|
|
cp_time_n = dfpl->cp_time_n;
|
|
|
|
cp_time_o = dfpl->cp_time_o;
|
|
|
|
} else {
|
|
|
|
if (i == 0 ) {
|
2020-10-31 21:14:27 +00:00
|
|
|
// average
|
|
|
|
cp_time_n = dfpl->cp_time_n;
|
|
|
|
cp_time_o = dfpl->cp_time_o;
|
2017-04-19 14:12:17 +00:00
|
|
|
} else {
|
2020-10-31 21:14:27 +00:00
|
|
|
// specific smp cores
|
|
|
|
cp_times_offset = i - 1;
|
|
|
|
cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES);
|
|
|
|
cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES);
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// diff old vs new
|
|
|
|
unsigned long long total_o = 0;
|
|
|
|
unsigned long long total_n = 0;
|
|
|
|
unsigned long long total_d = 0;
|
|
|
|
for (int s = 0; s < CPUSTATES; s++) {
|
2020-10-31 21:14:27 +00:00
|
|
|
cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
|
|
|
|
total_o += cp_time_o[s];
|
|
|
|
total_n += cp_time_n[s];
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// totals
|
|
|
|
total_d = total_n - total_o;
|
2020-11-01 00:09:51 +00:00
|
|
|
if (total_d < 1 ) {
|
|
|
|
total_d = 1;
|
|
|
|
}
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
// save current state as old and calc percentages
|
|
|
|
for (int s = 0; s < CPUSTATES; ++s) {
|
2020-10-31 21:14:27 +00:00
|
|
|
cp_time_o[s] = cp_time_n[s];
|
|
|
|
cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CPUData* cpuData = &(dfpl->cpus[i]);
|
|
|
|
cpuData->userPercent = cp_time_p[CP_USER];
|
|
|
|
cpuData->nicePercent = cp_time_p[CP_NICE];
|
|
|
|
cpuData->systemPercent = cp_time_p[CP_SYS];
|
|
|
|
cpuData->irqPercent = cp_time_p[CP_INTR];
|
|
|
|
cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR];
|
|
|
|
// this one is not really used, but we store it anyway
|
|
|
|
cpuData->idlePercent = cp_time_p[CP_IDLE];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
|
|
|
|
DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl;
|
|
|
|
|
|
|
|
// @etosan:
|
|
|
|
// memory counter relationships seem to be these:
|
|
|
|
// total = active + wired + inactive + cache + free
|
|
|
|
// htop_used (unavail to anybody) = active + wired
|
|
|
|
// htop_cache (for cache meter) = buffers + cache
|
|
|
|
// user_free (avail to procs) = buffers + inactive + cache + free
|
|
|
|
size_t len = sizeof(pl->totalMem);
|
|
|
|
|
|
|
|
//disabled for now, as it is always smaller than phycal amount of memory...
|
|
|
|
//...to avoid "where is my memory?" questions
|
|
|
|
//sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0);
|
|
|
|
//pl->totalMem *= pageSizeKb;
|
|
|
|
sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0);
|
|
|
|
pl->totalMem /= 1024;
|
|
|
|
|
|
|
|
sysctl(MIB_vm_stats_vm_v_active_count, 4, &(dfpl->memActive), &len, NULL, 0);
|
|
|
|
dfpl->memActive *= pageSizeKb;
|
|
|
|
|
|
|
|
sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(dfpl->memWire), &len, NULL, 0);
|
|
|
|
dfpl->memWire *= pageSizeKb;
|
|
|
|
|
|
|
|
sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0);
|
|
|
|
pl->buffersMem /= 1024;
|
|
|
|
|
|
|
|
sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0);
|
|
|
|
pl->cachedMem *= pageSizeKb;
|
|
|
|
pl->usedMem = dfpl->memActive + dfpl->memWire;
|
|
|
|
|
|
|
|
struct kvm_swap swap[16];
|
2020-10-03 20:00:27 +00:00
|
|
|
int nswap = kvm_getswapinfo(dfpl->kd, swap, ARRAYSIZE(swap), 0);
|
2017-04-19 14:12:17 +00:00
|
|
|
pl->totalSwap = 0;
|
|
|
|
pl->usedSwap = 0;
|
|
|
|
for (int i = 0; i < nswap; i++) {
|
|
|
|
pl->totalSwap += swap[i].ksw_total;
|
|
|
|
pl->usedSwap += swap[i].ksw_used;
|
|
|
|
}
|
|
|
|
pl->totalSwap *= pageSizeKb;
|
|
|
|
pl->usedSwap *= pageSizeKb;
|
|
|
|
}
|
|
|
|
|
2021-05-19 15:44:37 +00:00
|
|
|
//static void DragonFlyBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) {
|
|
|
|
// const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, kproc->kp_pid };
|
|
|
|
// char buffer[2048];
|
|
|
|
// size_t size = sizeof(buffer);
|
|
|
|
// if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) {
|
|
|
|
// Process_updateExe(proc, NULL);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// /* Kernel threads return an empty buffer */
|
|
|
|
// if (buffer[0] == '\0') {
|
|
|
|
// Process_updateExe(proc, NULL);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Process_updateExe(proc, buffer);
|
|
|
|
//}
|
|
|
|
|
|
|
|
static void DragonFlyBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) {
|
|
|
|
if (Process_isKernelThread(proc))
|
|
|
|
return;
|
|
|
|
|
|
|
|
char path[32];
|
|
|
|
xSnprintf(path, sizeof(path), "/proc/%d/file", kproc->kp_pid);
|
|
|
|
|
|
|
|
char target[PATH_MAX];
|
|
|
|
ssize_t ret = readlink(path, target, sizeof(target) - 1);
|
|
|
|
if (ret <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
target[ret] = '\0';
|
|
|
|
Process_updateExe(proc, target);
|
|
|
|
}
|
|
|
|
|
2021-05-25 17:05:16 +00:00
|
|
|
static void DragonFlyBSDProcessList_updateCwd(const struct kinfo_proc* kproc, Process* proc) {
|
|
|
|
const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, kproc->kp_pid };
|
|
|
|
char buffer[2048];
|
|
|
|
size_t size = sizeof(buffer);
|
|
|
|
if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) {
|
|
|
|
free(proc->procCwd);
|
|
|
|
proc->procCwd = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Kernel threads return an empty buffer */
|
|
|
|
if (buffer[0] == '\0') {
|
|
|
|
free(proc->procCwd);
|
|
|
|
proc->procCwd = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
free_and_xStrdup(&proc->procCwd, buffer);
|
|
|
|
}
|
|
|
|
|
2021-05-19 15:44:37 +00:00
|
|
|
static void DragonFlyBSDProcessList_updateProcessName(kvm_t* kd, const struct kinfo_proc* kproc, Process* proc) {
|
|
|
|
Process_updateComm(proc, kproc->kp_comm);
|
|
|
|
|
2017-04-19 14:12:17 +00:00
|
|
|
char** argv = kvm_getargv(kd, kproc, 0);
|
2021-05-19 15:44:37 +00:00
|
|
|
if (!argv || !argv[0]) {
|
|
|
|
Process_updateCmdline(proc, kproc->kp_comm, 0, strlen(kproc->kp_comm));
|
|
|
|
return;
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
2021-05-19 15:44:37 +00:00
|
|
|
|
|
|
|
size_t len = 0;
|
2017-04-19 14:12:17 +00:00
|
|
|
for (int i = 0; argv[i]; i++) {
|
|
|
|
len += strlen(argv[i]) + 1;
|
|
|
|
}
|
2021-05-19 15:44:37 +00:00
|
|
|
|
|
|
|
char* cmdline = xMalloc(len);
|
|
|
|
char* at = cmdline;
|
|
|
|
int end = 0;
|
2017-04-19 14:12:17 +00:00
|
|
|
for (int i = 0; argv[i]; i++) {
|
|
|
|
at = stpcpy(at, argv[i]);
|
2021-05-19 15:44:37 +00:00
|
|
|
if (end == 0) {
|
|
|
|
end = at - cmdline;
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
2021-05-19 15:44:37 +00:00
|
|
|
*at++ = ' ';
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
at--;
|
|
|
|
*at = '\0';
|
2021-05-19 15:44:37 +00:00
|
|
|
|
|
|
|
Process_updateCmdline(proc, cmdline, 0, end);
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) {
|
|
|
|
size_t len;
|
2020-10-31 22:28:02 +00:00
|
|
|
char* jls; /* Jail list */
|
|
|
|
char* curpos;
|
|
|
|
char* nextpos;
|
2017-04-20 13:14:33 +00:00
|
|
|
|
|
|
|
if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
|
2021-01-07 15:08:43 +00:00
|
|
|
CRT_fatalError("initial sysctlbyname / jail.list failed");
|
2017-04-20 13:14:33 +00:00
|
|
|
}
|
2021-05-21 20:42:00 +00:00
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
retry:
|
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
jls = xMalloc(len);
|
2021-01-07 15:08:43 +00:00
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) {
|
|
|
|
if (errno == ENOMEM) {
|
|
|
|
free(jls);
|
|
|
|
goto retry;
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
2021-01-07 15:08:43 +00:00
|
|
|
CRT_fatalError("sysctlbyname / jail.list failed");
|
2017-04-20 13:14:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dfpl->jails) {
|
|
|
|
Hashtable_delete(dfpl->jails);
|
|
|
|
}
|
2021-05-21 20:42:00 +00:00
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
dfpl->jails = Hashtable_new(20, true);
|
|
|
|
curpos = jls;
|
|
|
|
while (curpos) {
|
|
|
|
int jailid;
|
2020-10-31 22:28:02 +00:00
|
|
|
char* str_hostname;
|
2021-05-21 20:42:00 +00:00
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
nextpos = strchr(curpos, '\n');
|
2020-11-01 00:09:51 +00:00
|
|
|
if (nextpos) {
|
2017-04-20 13:14:33 +00:00
|
|
|
*nextpos++ = 0;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2017-04-20 13:14:33 +00:00
|
|
|
|
|
|
|
jailid = atoi(strtok(curpos, " "));
|
|
|
|
str_hostname = strtok(NULL, " ");
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
char* jname = (char*) (Hashtable_get(dfpl->jails, jailid));
|
2017-04-20 13:14:33 +00:00
|
|
|
if (jname == NULL) {
|
|
|
|
jname = xStrdup(str_hostname);
|
|
|
|
Hashtable_put(dfpl->jails, jailid, jname);
|
|
|
|
}
|
|
|
|
|
|
|
|
curpos = nextpos;
|
2020-10-31 21:14:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(jls);
|
2017-04-20 13:14:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-20 17:32:04 +00:00
|
|
|
static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
|
2017-04-20 13:14:33 +00:00
|
|
|
char* hostname;
|
|
|
|
char* jname;
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
if (jailid != 0 && dfpl->jails && (hostname = (char*)Hashtable_get(dfpl->jails, jailid))) {
|
2017-04-20 13:14:33 +00:00
|
|
|
jname = xStrdup(hostname);
|
2017-04-19 14:12:17 +00:00
|
|
|
} else {
|
2017-04-20 13:14:33 +00:00
|
|
|
jname = xStrdup("-");
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
2021-05-21 20:42:00 +00:00
|
|
|
|
2017-04-19 14:12:17 +00:00
|
|
|
return jname;
|
|
|
|
}
|
|
|
|
|
2020-10-27 20:26:28 +00:00
|
|
|
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
|
|
|
|
DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) super;
|
|
|
|
const Settings* settings = super->settings;
|
2017-04-19 14:12:17 +00:00
|
|
|
bool hideKernelThreads = settings->hideKernelThreads;
|
|
|
|
bool hideUserlandThreads = settings->hideUserlandThreads;
|
|
|
|
|
2020-10-27 20:26:28 +00:00
|
|
|
DragonFlyBSDProcessList_scanMemoryInfo(super);
|
|
|
|
DragonFlyBSDProcessList_scanCPUTime(super);
|
2017-04-20 13:14:33 +00:00
|
|
|
DragonFlyBSDProcessList_scanJails(dfpl);
|
2017-04-19 14:12:17 +00:00
|
|
|
|
2020-10-13 14:03:37 +00:00
|
|
|
// in pause mode only gather global data for meters (CPU/memory/...)
|
2020-11-01 00:09:51 +00:00
|
|
|
if (pauseProcessUpdate) {
|
2020-10-13 14:03:37 +00:00
|
|
|
return;
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2020-10-13 14:03:37 +00:00
|
|
|
|
2017-04-19 14:12:17 +00:00
|
|
|
int count = 0;
|
|
|
|
|
2020-12-20 17:32:04 +00:00
|
|
|
const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
2020-12-20 17:32:04 +00:00
|
|
|
const struct kinfo_proc* kproc = &kprocs[i];
|
2017-04-19 14:12:17 +00:00
|
|
|
bool preExisting = false;
|
2020-09-09 19:35:15 +00:00
|
|
|
bool ATTR_UNUSED isIdleProcess = false;
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
// note: dragonflybsd kernel processes all have the same pid, so we misuse the kernel thread address to give them a unique identifier
|
2020-10-27 20:26:28 +00:00
|
|
|
Process* proc = ProcessList_getProcess(super, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, DragonFlyBSDProcess_new);
|
2017-04-19 14:12:17 +00:00
|
|
|
DragonFlyBSDProcess* dfp = (DragonFlyBSDProcess*) proc;
|
|
|
|
|
|
|
|
if (!preExisting) {
|
|
|
|
dfp->jid = kproc->kp_jailid;
|
|
|
|
if (kproc->kp_ktaddr && kproc->kp_flags & P_SYSTEM) {
|
|
|
|
// dfb kernel threads all have the same pid, so we misuse the kernel thread address to give them a unique identifier
|
|
|
|
proc->pid = (pid_t)kproc->kp_ktaddr;
|
2021-04-10 12:08:26 +00:00
|
|
|
proc->isKernelThread = true;
|
2017-04-19 14:12:17 +00:00
|
|
|
} else {
|
|
|
|
proc->pid = kproc->kp_pid; // process ID
|
2021-04-10 12:08:26 +00:00
|
|
|
proc->isKernelThread = false;
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
2021-04-10 12:08:26 +00:00
|
|
|
proc->isUserlandThread = kproc->kp_nthreads > 1;
|
|
|
|
proc->ppid = kproc->kp_ppid; // parent process id
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->tpgid = kproc->kp_tpgid; // tty process group id
|
2017-04-21 14:34:40 +00:00
|
|
|
//proc->tgid = kproc->kp_lwp.kl_tid; // thread group id
|
|
|
|
proc->tgid = kproc->kp_pid; // thread group id
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->pgrp = kproc->kp_pgid; // process group id
|
|
|
|
proc->session = kproc->kp_sid;
|
|
|
|
proc->st_uid = kproc->kp_uid; // user ID
|
|
|
|
proc->processor = kproc->kp_lwp.kl_origcpu;
|
|
|
|
proc->starttime_ctime = kproc->kp_start.tv_sec;
|
2021-05-19 15:34:41 +00:00
|
|
|
Process_fillStarttimeBuffer(proc);
|
2020-10-27 20:26:28 +00:00
|
|
|
proc->user = UsersTable_getRef(super->usersTable, proc->st_uid);
|
2017-04-19 14:12:17 +00:00
|
|
|
|
2021-03-21 18:40:56 +00:00
|
|
|
proc->tty_nr = kproc->kp_tdev; // control terminal device number
|
|
|
|
const char* name = (kproc->kp_tdev != NODEV) ? devname(kproc->kp_tdev, S_IFCHR) : NULL;
|
|
|
|
if (!name) {
|
|
|
|
free(proc->tty_name);
|
|
|
|
proc->tty_name = NULL;
|
|
|
|
} else {
|
|
|
|
free_and_xStrdup(&proc->tty_name, name);
|
|
|
|
}
|
|
|
|
|
2021-05-19 15:44:37 +00:00
|
|
|
DragonFlyBSDProcessList_updateExe(kproc, proc);
|
|
|
|
DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc);
|
|
|
|
|
2021-05-25 17:05:16 +00:00
|
|
|
if (settings->flags & PROCESS_FLAG_CWD) {
|
|
|
|
DragonFlyBSDProcessList_updateCwd(kproc, proc);
|
|
|
|
}
|
|
|
|
|
2020-10-27 20:26:28 +00:00
|
|
|
ProcessList_add(super, proc);
|
2021-04-18 16:10:04 +00:00
|
|
|
|
2017-04-20 13:14:33 +00:00
|
|
|
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
|
2017-04-19 14:12:17 +00:00
|
|
|
} else {
|
|
|
|
proc->processor = kproc->kp_lwp.kl_cpuid;
|
2020-10-31 19:52:20 +00:00
|
|
|
if (dfp->jid != kproc->kp_jailid) { // process can enter jail anytime
|
2017-04-19 14:12:17 +00:00
|
|
|
dfp->jid = kproc->kp_jailid;
|
|
|
|
free(dfp->jname);
|
2017-04-20 13:14:33 +00:00
|
|
|
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
2020-11-21 23:55:42 +00:00
|
|
|
// if there are reapers in the system, process can get reparented anytime
|
|
|
|
proc->ppid = kproc->kp_ppid;
|
2020-10-31 19:52:20 +00:00
|
|
|
if (proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs)
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->st_uid = kproc->kp_uid;
|
2020-10-27 20:26:28 +00:00
|
|
|
proc->user = UsersTable_getRef(super->usersTable, proc->st_uid);
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
if (settings->updateProcessNames) {
|
2021-05-19 15:44:37 +00:00
|
|
|
DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc);
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 00:57:48 +00:00
|
|
|
proc->m_virt = kproc->kp_vm_map_size / ONE_K;
|
2020-12-20 17:21:25 +00:00
|
|
|
proc->m_resident = kproc->kp_vm_rssize * pageSizeKb;
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->nlwp = kproc->kp_nthreads; // number of lwp thread
|
|
|
|
proc->time = (kproc->kp_swtime + 5000) / 10000;
|
|
|
|
|
|
|
|
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
|
2020-12-10 00:57:48 +00:00
|
|
|
proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
|
2017-04-19 14:12:17 +00:00
|
|
|
|
|
|
|
if (proc->percent_cpu > 0.1) {
|
|
|
|
// system idle process should own all CPU time left regardless of CPU count
|
2021-01-27 14:12:06 +00:00
|
|
|
if (String_eq("idle", kproc->kp_comm)) {
|
2017-04-19 14:12:17 +00:00
|
|
|
isIdleProcess = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kproc->kp_lwp.kl_pid != -1)
|
|
|
|
proc->priority = kproc->kp_lwp.kl_prio;
|
|
|
|
else
|
|
|
|
proc->priority = -kproc->kp_lwp.kl_tdprio;
|
|
|
|
|
|
|
|
switch(kproc->kp_lwp.kl_rtprio.type) {
|
2021-05-21 20:42:00 +00:00
|
|
|
case RTP_PRIO_REALTIME:
|
|
|
|
proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX + kproc->kp_lwp.kl_rtprio.prio;
|
|
|
|
break;
|
|
|
|
case RTP_PRIO_IDLE:
|
|
|
|
proc->nice = PRIO_MAX + 1 + kproc->kp_lwp.kl_rtprio.prio;
|
|
|
|
break;
|
|
|
|
case RTP_PRIO_THREAD:
|
|
|
|
proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX - kproc->kp_lwp.kl_rtprio.prio;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
proc->nice = kproc->kp_nice;
|
|
|
|
break;
|
2017-04-19 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// would be nice if we could store multiple states in proc->state (as enum) and have writeField render them
|
|
|
|
switch (kproc->kp_stat) {
|
|
|
|
case SIDL: proc->state = 'I'; isIdleProcess = true; break;
|
|
|
|
case SACTIVE:
|
|
|
|
switch (kproc->kp_lwp.kl_stat) {
|
|
|
|
case LSSLEEP:
|
2020-11-18 12:59:55 +00:00
|
|
|
if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptible wait short/long
|
2017-04-19 14:12:17 +00:00
|
|
|
if (kproc->kp_lwp.kl_slptime >= MAXSLP) {
|
|
|
|
proc->state = 'I';
|
|
|
|
isIdleProcess = true;
|
|
|
|
} else {
|
|
|
|
proc->state = 'S';
|
|
|
|
}
|
2020-11-18 12:59:55 +00:00
|
|
|
else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptible lwkt wait
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->state = 'S';
|
2020-11-18 12:59:55 +00:00
|
|
|
else if (kproc->kp_paddr) // uninterruptible wait
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->state = 'D';
|
2020-11-18 12:59:55 +00:00
|
|
|
else // uninterruptible lwkt wait
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->state = 'B';
|
|
|
|
break;
|
|
|
|
case LSRUN:
|
|
|
|
if (kproc->kp_lwp.kl_stat == LSRUN) {
|
|
|
|
if (!(kproc->kp_lwp.kl_tdflags & (TDF_RUNNING | TDF_RUNQ)))
|
|
|
|
proc->state = 'Q';
|
|
|
|
else
|
|
|
|
proc->state = 'R';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LSSTOP:
|
|
|
|
proc->state = 'T';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
proc->state = 'A';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSTOP: proc->state = 'T'; break;
|
|
|
|
case SZOMB: proc->state = 'Z'; break;
|
|
|
|
case SCORE: proc->state = 'C'; break;
|
|
|
|
default: proc->state = '?';
|
|
|
|
}
|
|
|
|
|
2021-05-21 20:42:00 +00:00
|
|
|
if (kproc->kp_flags & P_SWAPPEDOUT)
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->state = 'W';
|
2021-05-21 20:42:00 +00:00
|
|
|
if (kproc->kp_flags & P_TRACED)
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->state = 'T';
|
2021-05-21 20:42:00 +00:00
|
|
|
if (kproc->kp_flags & P_JAILED)
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->state = 'J';
|
2019-10-31 16:39:12 +00:00
|
|
|
|
2021-05-19 15:44:37 +00:00
|
|
|
if (Process_isKernelThread(proc))
|
2020-10-27 20:26:28 +00:00
|
|
|
super->kernelThreads++;
|
2017-04-19 14:12:17 +00:00
|
|
|
|
2020-10-27 20:26:28 +00:00
|
|
|
super->totalTasks++;
|
2021-05-21 20:42:00 +00:00
|
|
|
|
2017-04-19 14:12:17 +00:00
|
|
|
if (proc->state == 'R')
|
2020-10-27 20:26:28 +00:00
|
|
|
super->runningTasks++;
|
2021-05-21 20:42:00 +00:00
|
|
|
|
2021-06-13 09:37:21 +00:00
|
|
|
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
|
2017-04-19 14:12:17 +00:00
|
|
|
proc->updated = true;
|
|
|
|
}
|
|
|
|
}
|
2021-06-12 20:04:37 +00:00
|
|
|
|
|
|
|
bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) {
|
|
|
|
assert(id < super->existingCPUs);
|
|
|
|
|
|
|
|
// TODO: support offline CPUs and hot swapping
|
|
|
|
(void) super; (void) id;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|