From 72235d8e098d9d79029dca65122605741e1aafad Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 2 May 2023 16:56:18 +1000 Subject: Adapt platform code for the new Machine base class Move host-centric data to new derived Machine classes, separate from process-list-centric data. --- CommandLine.c | 6 +- Machine.h | 2 + Makefile.am | 18 + ProcessList.c | 10 +- ProcessList.h | 6 +- ScreenManager.c | 7 +- darwin/DarwinMachine.c | 113 +++++ darwin/DarwinMachine.h | 28 ++ darwin/DarwinProcess.c | 5 +- darwin/DarwinProcessList.c | 102 +---- darwin/DarwinProcessList.h | 21 - darwin/Platform.c | 23 +- dragonflybsd/DragonFlyBSDMachine.c | 341 +++++++++++++++ dragonflybsd/DragonFlyBSDMachine.h | 61 +++ dragonflybsd/DragonFlyBSDProcessList.c | 353 +-------------- dragonflybsd/DragonFlyBSDProcessList.h | 47 -- dragonflybsd/Platform.c | 27 +- freebsd/FreeBSDMachine.c | 396 +++++++++++++++++ freebsd/FreeBSDMachine.h | 54 +++ freebsd/FreeBSDProcessList.c | 386 +---------------- freebsd/FreeBSDProcessList.h | 41 -- freebsd/Platform.c | 32 +- linux/HugePageMeter.c | 8 +- linux/LibSensors.c | 2 +- linux/LibSensors.h | 2 +- linux/LinuxMachine.c | 707 ++++++++++++++++++++++++++++++ linux/LinuxMachine.h | 110 +++++ linux/LinuxProcess.c | 14 +- linux/LinuxProcessList.c | 763 ++------------------------------- linux/LinuxProcessList.h | 92 ---- linux/Platform.c | 53 +-- netbsd/NetBSDMachine.c | 282 ++++++++++++ netbsd/NetBSDMachine.h | 54 +++ netbsd/NetBSDProcessList.c | 280 +----------- netbsd/NetBSDProcessList.h | 40 -- netbsd/Platform.c | 7 +- openbsd/OpenBSDMachine.c | 286 ++++++++++++ openbsd/OpenBSDMachine.h | 53 +++ openbsd/OpenBSDProcessList.c | 299 +------------ openbsd/OpenBSDProcessList.h | 43 -- openbsd/Platform.c | 9 +- pcp/PCPMachine.c | 332 ++++++++++++++ pcp/PCPMachine.h | 68 +++ pcp/PCPProcessList.c | 369 ++-------------- pcp/PCPProcessList.h | 52 --- pcp/Platform.c | 32 +- solaris/Platform.c | 21 +- solaris/SolarisMachine.c | 332 ++++++++++++++ solaris/SolarisMachine.h | 59 +++ solaris/SolarisProcessList.c | 332 +------------- solaris/SolarisProcessList.h | 36 -- unsupported/UnsupportedMachine.c | 56 +++ unsupported/UnsupportedMachine.h | 17 + unsupported/UnsupportedProcessList.c | 35 +- unsupported/UnsupportedProcessList.h | 14 +- 55 files changed, 3672 insertions(+), 3266 deletions(-) create mode 100644 darwin/DarwinMachine.c create mode 100644 darwin/DarwinMachine.h create mode 100644 dragonflybsd/DragonFlyBSDMachine.c create mode 100644 dragonflybsd/DragonFlyBSDMachine.h create mode 100644 freebsd/FreeBSDMachine.c create mode 100644 freebsd/FreeBSDMachine.h create mode 100644 linux/LinuxMachine.c create mode 100644 linux/LinuxMachine.h create mode 100644 netbsd/NetBSDMachine.c create mode 100644 netbsd/NetBSDMachine.h create mode 100644 openbsd/OpenBSDMachine.c create mode 100644 openbsd/OpenBSDMachine.h create mode 100644 pcp/PCPMachine.c create mode 100644 pcp/PCPMachine.h create mode 100644 solaris/SolarisMachine.c create mode 100644 solaris/SolarisMachine.h create mode 100644 unsupported/UnsupportedMachine.c create mode 100644 unsupported/UnsupportedMachine.h diff --git a/CommandLine.c b/CommandLine.c index 9e2018ef..8095fa8f 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -383,9 +383,11 @@ int CommandLine_run(int argc, char** argv) { ScreenManager* scr = ScreenManager_new(header, host, &state, true); ScreenManager_add(scr, (Panel*) panel, -1); - ProcessList_scan(pl, false); + Machine_scan(host); + ProcessList_scan(pl); CommandLine_delay(host, 75); - ProcessList_scan(pl, false); + Machine_scan(host); + ProcessList_scan(pl); if (settings->ss->allBranchesCollapsed) ProcessList_collapseAllBranches(pl); diff --git a/Machine.h b/Machine.h index 3683701a..4628d686 100644 --- a/Machine.h +++ b/Machine.h @@ -86,4 +86,6 @@ bool Machine_isCPUonline(const Machine* this, unsigned int id); void Machine_addList(Machine* this, struct ProcessList_ *pl); +void Machine_scan(Machine* this); + #endif diff --git a/Makefile.am b/Makefile.am index 94db4d77..b25d1cb8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -166,6 +166,7 @@ linux_platform_headers = \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ linux/LibSensors.h \ + linux/LinuxMachine.h \ linux/LinuxProcess.h \ linux/LinuxProcessList.h \ linux/Platform.h \ @@ -188,6 +189,7 @@ linux_platform_sources = \ linux/HugePageMeter.c \ linux/IOPriorityPanel.c \ linux/LibSensors.c \ + linux/LinuxMachine.c \ linux/LinuxProcess.c \ linux/LinuxProcessList.c \ linux/Platform.c \ @@ -208,6 +210,7 @@ endif # ------- freebsd_platform_headers = \ + freebsd/FreeBSDMachine.h \ freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcess.h \ freebsd/Platform.h \ @@ -223,6 +226,7 @@ freebsd_platform_headers = \ freebsd_platform_sources = \ freebsd/Platform.c \ + freebsd/FreeBSDMachine.c \ freebsd/FreeBSDProcessList.c \ freebsd/FreeBSDProcess.c \ generic/fdstat_sysctl.c \ @@ -242,6 +246,7 @@ endif # ------------ dragonflybsd_platform_headers = \ + dragonflybsd/DragonFlyBSDMachine.h \ dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcess.h \ dragonflybsd/Platform.h \ @@ -252,6 +257,7 @@ dragonflybsd_platform_headers = \ generic/uname.h dragonflybsd_platform_sources = \ + dragonflybsd/DragonFlyBSDMachine.c \ dragonflybsd/DragonFlyBSDProcessList.c \ dragonflybsd/DragonFlyBSDProcess.c \ dragonflybsd/Platform.c \ @@ -275,6 +281,7 @@ netbsd_platform_headers = \ generic/uname.h \ netbsd/Platform.h \ netbsd/ProcessField.h \ + netbsd/NetBSDMachine.h \ netbsd/NetBSDProcess.h \ netbsd/NetBSDProcessList.h @@ -284,6 +291,7 @@ netbsd_platform_sources = \ generic/hostname.c \ generic/uname.c \ netbsd/Platform.c \ + netbsd/NetBSDMachine.c \ netbsd/NetBSDProcess.c \ netbsd/NetBSDProcessList.c @@ -299,6 +307,7 @@ openbsd_platform_headers = \ generic/gettime.h \ generic/hostname.h \ generic/uname.h \ + openbsd/OpenBSDMachine.h \ openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcess.h \ openbsd/Platform.h \ @@ -308,6 +317,7 @@ openbsd_platform_sources = \ generic/gettime.c \ generic/hostname.c \ generic/uname.c \ + openbsd/OpenBSDMachine.c \ openbsd/OpenBSDProcessList.c \ openbsd/OpenBSDProcess.c \ openbsd/Platform.c @@ -321,6 +331,7 @@ endif # ------ darwin_platform_headers = \ + darwin/DarwinMachine.h \ darwin/DarwinProcess.h \ darwin/DarwinProcessList.h \ darwin/Platform.h \ @@ -338,6 +349,7 @@ darwin_platform_headers = \ darwin_platform_sources = \ darwin/Platform.c \ darwin/PlatformHelpers.c \ + darwin/DarwinMachine.c \ darwin/DarwinProcess.c \ darwin/DarwinProcessList.c \ generic/fdstat_sysctl.c \ @@ -363,6 +375,7 @@ solaris_platform_headers = \ generic/uname.h \ solaris/ProcessField.h \ solaris/Platform.h \ + solaris/SolarisMachine.h \ solaris/SolarisProcess.h \ solaris/SolarisProcessList.h \ zfs/ZfsArcMeter.h \ @@ -374,6 +387,7 @@ solaris_platform_sources = \ generic/hostname.c \ generic/uname.c \ solaris/Platform.c \ + solaris/SolarisMachine.c \ solaris/SolarisProcess.c \ solaris/SolarisProcessList.c \ zfs/ZfsArcMeter.c \ @@ -393,6 +407,7 @@ pcp_platform_headers = \ linux/ZramStats.h \ pcp/PCPDynamicColumn.h \ pcp/PCPDynamicMeter.h \ + pcp/PCPMachine.h \ pcp/PCPMetric.h \ pcp/PCPProcess.h \ pcp/PCPProcessList.h \ @@ -407,6 +422,7 @@ pcp_platform_sources = \ linux/ZramMeter.c \ pcp/PCPDynamicColumn.c \ pcp/PCPDynamicMeter.c \ + pcp/PCPMachine.c \ pcp/PCPMetric.c \ pcp/PCPProcess.c \ pcp/PCPProcessList.c \ @@ -427,12 +443,14 @@ unsupported_platform_headers = \ generic/gettime.h \ unsupported/Platform.h \ unsupported/ProcessField.h \ + unsupported/UnsupportedMachine.h \ unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcessList.h unsupported_platform_sources = \ generic/gettime.c \ unsupported/Platform.c \ + unsupported/UnsupportedMachine.c \ unsupported/UnsupportedProcess.c \ unsupported/UnsupportedProcessList.c diff --git a/ProcessList.c b/ProcessList.c index 49217b8c..58e63d03 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -410,13 +410,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, return proc; } -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; - } - +void ProcessList_scan(ProcessList* this) { // mark all process as "dirty" for (int i = 0; i < Vector_size(this->processes); i++) { Process* p = (Process*) Vector_get(this->processes, i); @@ -442,7 +436,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { firstScanDone = true; } - ProcessList_goThroughEntries(this, false); + ProcessList_goThroughEntries(this); uid_t maxUid = 0; const Settings* settings = host->settings; diff --git a/ProcessList.h b/ProcessList.h index d09cc072..0f0f7d51 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -49,8 +49,8 @@ typedef struct ProcessList_ { /* Implemented by platforms */ ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); -void ProcessList_delete(ProcessList* pl); -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +void ProcessList_delete(ProcessList* this); +void ProcessList_goThroughEntries(ProcessList* this); void ProcessList_init(ProcessList* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList); @@ -74,7 +74,7 @@ void ProcessList_rebuildPanel(ProcessList* this); Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); -void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate); +void ProcessList_scan(ProcessList* this); static inline Process* ProcessList_findProcess(ProcessList* this, pid_t pid) { return (Process*) Hashtable_get(this->processTable, pid); diff --git a/ScreenManager.c b/ScreenManager.c index f1897893..18e09343 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "CRT.h" #include "FunctionBar.h" +#include "Machine.h" #include "Macros.h" #include "Object.h" #include "Platform.h" @@ -135,8 +136,10 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi host->pl->needsSort = true; *sortTimeout = 1; } - // scan processes first - some header values are calculated there - ProcessList_scan(host->pl, this->state->pauseUpdate); + // sample current values for system metrics and processes if not paused + Machine_scan(host); + if (!this->state->pauseUpdate) + ProcessList_scan(host->pl); // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c new file mode 100644 index 00000000..6bf52b76 --- /dev/null +++ b/darwin/DarwinMachine.c @@ -0,0 +1,113 @@ +/* +htop - DarwinMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "darwin/DarwinMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Machine.h" +#include "darwin/Platform.h" +#include "darwin/PlatformHelpers.h" +#include "generic/openzfs_sysctl.h" +#include "zfs/ZfsArcStats.h" + + +static void DarwinMachine_getHostInfo(host_basic_info_data_t* p) { + mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; + + if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { + CRT_fatalError("Unable to retrieve host info"); + } +} + +static void DarwinMachine_freeCPULoadInfo(processor_cpu_load_info_t* p) { + if (NULL != p && NULL != *p) { + if (0 != munmap(*p, vm_page_size)) { + CRT_fatalError("Unable to free old CPU load information"); + } + *p = NULL; + } +} + +static unsigned DarwinMachine_allocateCPULoadInfo(processor_cpu_load_info_t* p) { + mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t); + unsigned cpu_count; + + // TODO Improving the accuracy of the load counts would help a lot. + if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { + CRT_fatalError("Unable to retrieve CPU info"); + } + + return cpu_count; +} + +static void DarwinMachine_getVMStats(vm_statistics_t p) { + mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; + + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { + CRT_fatalError("Unable to retrieve VM statistics"); + } +} + +void Machine_scan(Machine* super) { + DarwinMachine* host = (DarwinMachine*) super; + + /* Update the global data (CPU times and VM stats) */ + DarwinMachine_freeCPULoadInfo(&host->prev_load); + host->prev_load = host->curr_load; + DarwinMachine_allocateCPULoadInfo(&host->curr_load); + DarwinMachine_getVMStats(&host->vm_stats); + openzfs_sysctl_updateArcStats(&host->zfs); +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + DarwinMachine* this = xCalloc(1, sizeof(DarwinMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + /* Initialize the CPU information */ + super->activeCPUs = DarwinMachine_allocateCPULoadInfo(&this->prev_load); + super->existingCPUs = super->activeCPUs; + DarwinMachine_getHostInfo(&this->host_info); + DarwinMachine_allocateCPULoadInfo(&this->curr_load); + + /* Initialize the VM statistics */ + DarwinMachine_getVMStats(&this->vm_stats); + + /* Initialize the ZFS kstats, if zfs.kext loaded */ + openzfs_sysctl_init(&this->zfs); + openzfs_sysctl_updateArcStats(&this->zfs); + + return super; +} + +void Machine_delete(Machine* super) { + DarwinMachine* host = (DarwinMachine*) super; + + DarwinMachine_freeCPULoadInfo(&host->prev_load); + + Machine_done(super); + free(super); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) host; (void) id; + + return true; +} diff --git a/darwin/DarwinMachine.h b/darwin/DarwinMachine.h new file mode 100644 index 00000000..3135b589 --- /dev/null +++ b/darwin/DarwinMachine.h @@ -0,0 +1,28 @@ +#ifndef HEADER_DarwinMachine +#define HEADER_DarwinMachine +/* +htop - DarwinMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include + +#include "Machine.h" +#include "zfs/ZfsArcStats.h" + + +typedef struct DarwinMachine_ { + Machine super; + + host_basic_info_data_t host_info; + vm_statistics_data_t vm_stats; + processor_cpu_load_info_t prev_load; + processor_cpu_load_info_t curr_load; + + ZfsArcStats zfs; +} DarwinMachine; + +#endif diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 22004e36..e6366d70 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "CRT.h" #include "Process.h" +#include "darwin/DarwinMachine.h" #include "darwin/Platform.h" @@ -365,6 +366,8 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* struct proc_taskinfo pti; if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + const DarwinMachine* dhost = (const DarwinMachine*) proc->super.host; + uint64_t total_existing_time_ns = proc->stime + proc->utime; uint64_t user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user); @@ -386,7 +389,7 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* proc->super.m_resident = pti.pti_resident_size / ONE_K; proc->super.majflt = pti.pti_faults; proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 - / (double)dpl->host_info.max_mem; + / (double)dhost->host_info.max_mem; proc->stime = system_time_ns; proc->utime = user_time_ns; diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 16c44d2c..bf311dc7 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -20,6 +20,7 @@ in the source distribution for its full text. #include "CRT.h" #include "ProcessList.h" +#include "darwin/DarwinMachine.h" #include "darwin/DarwinProcess.h" #include "darwin/Platform.h" #include "darwin/PlatformHelpers.h" @@ -27,43 +28,6 @@ in the source distribution for its full text. #include "zfs/ZfsArcStats.h" -static void ProcessList_getHostInfo(host_basic_info_data_t* p) { - mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; - - if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { - CRT_fatalError("Unable to retrieve host info"); - } -} - -static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) { - if (NULL != p && NULL != *p) { - if (0 != munmap(*p, vm_page_size)) { - CRT_fatalError("Unable to free old CPU load information"); - } - *p = NULL; - } -} - -static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) { - mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t); - unsigned cpu_count; - - // TODO Improving the accuracy of the load counts woule help a lot. - if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { - CRT_fatalError("Unable to retrieve CPU info"); - } - - return cpu_count; -} - -static void ProcessList_getVMStats(vm_statistics_t p) { - mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; - - if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { - CRT_fatalError("Unable to retrieve VM statistics"); - } -} - static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; struct kinfo_proc* processes = NULL; @@ -91,29 +55,11 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); + ProcessList* super = &this->super; - ProcessList_init(&this->super, Class(DarwinProcess), host, pidMatchList); - - /* Initialize the CPU information */ - host->activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); - // TODO: support offline CPUs and hot swapping - host->existingCPUs = host->activeCPUs; - ProcessList_getHostInfo(&this->host_info); - ProcessList_allocateCPULoadInfo(&this->curr_load); - - /* Initialize the VM statistics */ - ProcessList_getVMStats(&this->vm_stats); - - /* Initialize the ZFS kstats, if zfs.kext loaded */ - openzfs_sysctl_init(&this->zfs); - openzfs_sysctl_updateArcStats(&this->zfs); + ProcessList_init(super, Class(DarwinProcess), host, pidMatchList); - this->super.kernelThreads = 0; - this->super.userlandThreads = 0; - this->super.totalTasks = 0; - this->super.runningTasks = 0; - - return &this->super; + return super; } void ProcessList_delete(ProcessList* this) { @@ -121,31 +67,20 @@ void ProcessList_delete(ProcessList* this) { free(this); } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - DarwinProcessList* dpl = (DarwinProcessList*)super; +void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; + const DarwinMachine* dhost = (const DarwinMachine*) host; + DarwinProcessList* dpl = (DarwinProcessList*) super; bool preExisting = true; struct kinfo_proc* ps; size_t count; DarwinProcess* proc; - /* Update the global data (CPU times and VM stats) */ - ProcessList_freeCPULoadInfo(&dpl->prev_load); - dpl->prev_load = dpl->curr_load; - ProcessList_allocateCPULoadInfo(&dpl->curr_load); - ProcessList_getVMStats(&dpl->vm_stats); - openzfs_sysctl_updateArcStats(&dpl->zfs); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - /* Get the time difference */ dpl->global_diff = 0; for (unsigned int i = 0; i < host->existingCPUs; ++i) { for (size_t j = 0; j < CPU_STATE_MAX; ++j) { - dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; + dpl->global_diff += dhost->curr_load[i].cpu_ticks[j] - dhost->prev_load[i].cpu_ticks[j]; } } @@ -178,7 +113,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } // Disabled for High Sierra due to bug in macOS High Sierra - bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); + bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); if (isScanThreadSupported) { DarwinProcess_scanThreads(proc); @@ -193,22 +128,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { free(ps); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) host; (void) id; - - return true; -} diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index 128896ae..56d6c1b5 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -11,33 +11,12 @@ in the source distribution for its full text. #include #include "ProcessList.h" -#include "zfs/ZfsArcStats.h" typedef struct DarwinProcessList_ { ProcessList super; - host_basic_info_data_t host_info; - vm_statistics_data_t vm_stats; - processor_cpu_load_info_t prev_load; - processor_cpu_load_info_t curr_load; - uint64_t kernel_threads; - uint64_t user_threads; uint64_t global_diff; - - ZfsArcStats zfs; } DarwinProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/darwin/Platform.c b/darwin/Platform.c index 71d08247..bb1ae92f 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -44,7 +44,7 @@ in the source distribution for its full text. #include "SysArchMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" -#include "darwin/DarwinProcessList.h" +#include "darwin/DarwinMachine.h" #include "darwin/PlatformHelpers.h" #include "generic/fdstat_sysctl.h" #include "zfs/ZfsArcMeter.h" @@ -257,9 +257,9 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { return Platform_setCPUAverageValues(mtr); } - const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->host->pl; - const processor_cpu_load_info_t prev = &dpl->prev_load[cpu - 1]; - const processor_cpu_load_info_t curr = &dpl->curr_load[cpu - 1]; + const DarwinMachine* dhost = (const DarwinMachine*) mtr->host; + const processor_cpu_load_info_t prev = &dhost->prev_load[cpu - 1]; + const processor_cpu_load_info_t curr = &dhost->curr_load[cpu - 1]; double total = 0; /* Take the sums */ @@ -286,12 +286,11 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { } void Platform_setMemoryValues(Meter* mtr) { - const Machine* host = mtr->host; - const DarwinProcessList* dpl = (const DarwinProcessList*) host->pl; - const struct vm_statistics* vm = &dpl->vm_stats; + const DarwinMachine* dhost = (const DarwinMachine*) mtr->host; + const struct vm_statistics* vm = &dhost->vm_stats; double page_K = (double)vm_page_size / (double)1024; - mtr->total = dpl->host_info.max_mem / 1024; + mtr->total = dhost->host_info.max_mem / 1024; mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K; mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; // mtr->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" @@ -313,15 +312,15 @@ void Platform_setSwapValues(Meter* mtr) { } void Platform_setZfsArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->host->pl; + const DarwinMachine* dhost = (const DarwinMachine*) this->host; - ZfsArcMeter_readStats(this, &(dpl->zfs)); + ZfsArcMeter_readStats(this, &dhost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->host->pl; + const DarwinMachine* dhost = (const DarwinMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(dpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &dhost->zfs); } char* Platform_getProcessEnv(pid_t pid) { diff --git a/dragonflybsd/DragonFlyBSDMachine.c b/dragonflybsd/DragonFlyBSDMachine.c new file mode 100644 index 00000000..fc7eb3b0 --- /dev/null +++ b/dragonflybsd/DragonFlyBSDMachine.c @@ -0,0 +1,341 @@ +/* +htop - DragonFlyBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2017 Diederik de Groot +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "dragonflybsd/DragonFlyBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Macros.h" + +#include "dragonflybsd/DragonFlyBSDProcess.h" + + +static int MIB_hw_physmem[2]; +static int MIB_vm_stats_vm_v_page_count[4]; + +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]; + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + size_t len; + char errbuf[_POSIX2_LINE_MAX]; + DragonFlyBSDMachine* this = xCalloc(1, sizeof(DragonFlyBSDMachine)); + Machine* super = &this->super; + + Machine_init(this, usersTable, userId); + + // 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(this->pageSize); + if (sysctlbyname("vm.stats.vm.v_page_size", &this->pageSize, &len, NULL, 0) == -1) + CRT_fatalError("Cannot get pagesize by sysctl"); + this->pageSizeKb = this->pageSize / ONE_K; + + // 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); + this->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); + this->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); + len = sizeof_cp_time_array; + + // fetch initial single (or average) CPU clicks from kernel + sysctl(MIB_kern_cp_time, 2, this->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); + this->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); + this->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); + len = cpus * sizeof_cp_time_array; + sysctl(MIB_kern_cp_times, 2, this->cp_times_o, &len, NULL, 0); + } + + super->existingCPUs = MAXIMUM(cpus, 1); + // TODO: support offline CPUs and hot swapping + super->activeCPUs = super->existingCPUs; + + if (cpus == 1 ) { + this->cpus = xRealloc(this->cpus, sizeof(CPUData)); + } else { + // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) + this->cpus = xRealloc(this->cpus, (super->existingCPUs + 1) * sizeof(CPUData)); + } + + 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; + } + + this->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return super; +} + +void Machine_delete(Machine* super) { + const DragonFlyBSDMachine* this = (DragonFlyBSDProcessList*) super; + + Machine_done(this); + + if (this->kd) { + kvm_close(this->kd); + } + + if (this->jails) { + Hashtable_delete(this->jails); + } + + free(this->cp_time_o); + free(this->cp_time_n); + free(this->cp_times_o); + free(this->cp_times_n); + free(this->cpus); + + free(this); +} + +static void DragonFlyBSDMachine_scanCPUTime(Machine* super) { + const DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super; + + unsigned int cpus = super->existingCPUs; // actual CPU count + unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) + int cp_times_offset; + + assert(cpus > 0); + + size_t sizeof_cp_time_array; + + unsigned long* cp_time_n; // old clicks state + unsigned long* cp_time_o; // current clicks state + + 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, this->cp_time_n, &sizeof_cp_time_array, NULL, 0); + + // get rest of CPUs + if (cpus > 1) { + // 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, this->cp_times_n, &sizeof_cp_time_array, NULL, 0); + } + + for (unsigned int i = 0; i < maxcpu; i++) { + if (cpus == 1) { + // single CPU box + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + if (i == 0 ) { + // average + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + // specific smp cores + cp_times_offset = i - 1; + cp_time_n = this->cp_times_n + (cp_times_offset * CPUSTATES); + cp_time_o = this->cp_times_o + (cp_times_offset * CPUSTATES); + } + } + + // 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++) { + cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; + total_o += cp_time_o[s]; + total_n += cp_time_n[s]; + } + + // totals + total_d = total_n - total_o; + if (total_d < 1 ) { + total_d = 1; + } + + // save current state as old and calc percentages + for (int s = 0; s < CPUSTATES; ++s) { + cp_time_o[s] = cp_time_n[s]; + cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; + } + + CPUData* cpuData = &(this->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 void DragonFlyBSDMachine_scanMemoryInfo(Machine* super) { + DragonFlyBSDMachine* this = (DragonFlyBSDProcessList*) super; + + // @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(super->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, &(super->totalMem), &len, NULL, 0); + pl->totalMem /= 1024; + + sysctl(MIB_vm_stats_vm_v_active_count, 4, &(this->memActive), &len, NULL, 0); + this->memActive *= this->pageSizeKb; + + sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(this->memWire), &len, NULL, 0); + this->memWire *= this->pageSizeKb; + + sysctl(MIB_vfs_bufspace, 2, &(super->buffersMem), &len, NULL, 0); + super->buffersMem /= 1024; + + sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(super->cachedMem), &len, NULL, 0); + super->cachedMem *= this->pageSizeKb; + super->usedMem = this->memActive + this->memWire; + + struct kvm_swap swap[16]; + int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0); + super->totalSwap = 0; + super->usedSwap = 0; + for (int i = 0; i < nswap; i++) { + super->totalSwap += swap[i].ksw_total; + super->usedSwap += swap[i].ksw_used; + } + super->totalSwap *= this->pageSizeKb; + super->usedSwap *= this->pageSizeKb; +} + +static void DragonFlyBSDMachine_scanJails(DragonFlyBSDMachine* this) { + size_t len; + char* jails; /* Jail list */ + char* curpos; + char* nextpos; + + if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) { + CRT_fatalError("initial sysctlbyname / jail.list failed"); + } + +retry: + if (len == 0) + return; + + jails = xMalloc(len); + + if (sysctlbyname("jail.list", jails, &len, NULL, 0) == -1) { + if (errno == ENOMEM) { + free(jails); + goto retry; + } + CRT_fatalError("sysctlbyname / jail.list failed"); + } + + if (this->jails) { + Hashtable_delete(this->jails); + } + + this->jails = Hashtable_new(20, true); + curpos = jails; + while (curpos) { + int jailid; + char* str_hostname; + + nextpos = strchr(curpos, '\n'); + if (nextpos) { + *nextpos++ = 0; + } + + jailid = atoi(strtok(curpos, " ")); + str_hostname = strtok(NULL, " "); + + char* jname = (char*) (Hashtable_get(this->jails, jailid)); + if (jname == NULL) { + jname = xStrdup(str_hostname); + Hashtable_put(this->jails, jailid, jname); + } + + curpos = nextpos; + } + + free(jails); +} + +char* DragonFlyBSDMachine_readJailName(DragonFlyBSDMachine* host, int jailid) { + char* hostname; + char* jname; + + if (jailid != 0 && host->jails && (hostname = (char*)Hashtable_get(host->jails, jailid))) { + jname = xStrdup(hostname); + } else { + jname = xStrdup("-"); + } + + return jname; +} + +void Machine_scan(Machine* super) { + DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super; + + DragonFlyBSDMachine_scanMemoryInfo(super); + DragonFlyBSDMachine_scanCPUTime(super); + DragonFlyBSDMachine_scanJails(this); +} diff --git a/dragonflybsd/DragonFlyBSDMachine.h b/dragonflybsd/DragonFlyBSDMachine.h new file mode 100644 index 00000000..276a73d7 --- /dev/null +++ b/dragonflybsd/DragonFlyBSDMachine.h @@ -0,0 +1,61 @@ +#ifndef HEADER_DragonFlyBSDMachine +#define HEADER_DragonFlyBSDMachine +/* +htop - DragonFlyBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2017 Diederik de Groot +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include // required for kvm.h +#include +#include +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "ProcessList.h" +#include "UsersTable.h" + + +typedef struct CPUData_ { + double userPercent; + double nicePercent; + double systemPercent; + double irqPercent; + double idlePercent; + double systemAllPercent; +} CPUData; + +typedef struct DragonFlyBSDMachine_ { + Machine super; + kvm_t* kd; + + Hashtable* jails; + + int pageSize; + int pageSizeKb; + int kernelFScale; + + unsigned long long int memWire; + unsigned long long int memActive; + unsigned long long int memInactive; + unsigned long long int memFree; + + CPUData* cpus; + + unsigned long* cp_time_o; + unsigned long* cp_time_n; + + unsigned long* cp_times_o; + unsigned long* cp_times_n; +} DragonFlyBSDMachine; + +char* DragonFlyBSDMachine_readJailName(DragonFlyBSDMachine* host, int jailid); + +#endif diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index c66917fb..4ff17932 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -22,248 +22,25 @@ in the source distribution for its full text. #include "CRT.h" #include "Macros.h" +#include "dragonflybsd/DragonFlyBSDMachine.h" #include "dragonflybsd/DragonFlyBSDProcess.h" -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; - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - size_t len; - char errbuf[_POSIX2_LINE_MAX]; - DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList)); - ProcessList* pl = (ProcessList*) dfpl; - ProcessList_init(pl, Class(DragonFlyBSDProcess), host, pidMatchList); - - // 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); - if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) - CRT_fatalError("Cannot get pagesize by sysctl"); - pageSizeKb = pageSize / ONE_K; - - // 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; - } + DragonFlyBSDProcessList* this = xCalloc(1, sizeof(DragonFlyBSDProcessList)); + ProcessList* super = (ProcessList*) this; - 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(CPUSTATES, sizeof(unsigned long)); - dfpl->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); - 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); - } - - host->existingCPUs = MAXIMUM(cpus, 1); - // TODO: support offline CPUs and hot swapping - host->activeCPUs = host->existingCPUs; - - if (cpus == 1 ) { - dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData)); - } else { - // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - dfpl->cpus = xRealloc(dfpl->cpus, (host->existingCPUs + 1) * sizeof(CPUData)); - } - - 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; - } + ProcessList_init(super, Class(DragonFlyBSDProcess), host, pidMatchList); - dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); - if (dfpl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - return pl; + return super; } -void ProcessList_delete(ProcessList* this) { - const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this; - if (dfpl->kd) { - kvm_close(dfpl->kd); - } - - if (dfpl->jails) { - Hashtable_delete(dfpl->jails); - } - 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); +void ProcessList_delete(ProcessList* super) { + const DragonFlyBSDProcessList* this = (DragonFlyBSDProcessList*) super; + ProcessList_done(super); free(this); } -static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) { - const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl; - - unsigned int cpus = pl->existingCPUs; // actual CPU count - unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) - int cp_times_offset; - - assert(cpus > 0); - - size_t sizeof_cp_time_array; - - unsigned long* cp_time_n; // old clicks state - unsigned long* cp_time_o; // current clicks state - - 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) { - // 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); - } - - for (unsigned int i = 0; i < maxcpu; i++) { - if (cpus == 1) { - // single CPU box - cp_time_n = dfpl->cp_time_n; - cp_time_o = dfpl->cp_time_o; - } else { - if (i == 0 ) { - // average - cp_time_n = dfpl->cp_time_n; - cp_time_o = dfpl->cp_time_o; - } else { - // 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); - } - } - - // 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++) { - cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; - total_o += cp_time_o[s]; - total_n += cp_time_n[s]; - } - - // totals - total_d = total_n - total_o; - if (total_d < 1 ) { - total_d = 1; - } - - // save current state as old and calc percentages - for (int s = 0; s < CPUSTATES; ++s) { - cp_time_o[s] = cp_time_n[s]; - cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; - } - - 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]; - int nswap = kvm_getswapinfo(dfpl->kd, swap, ARRAYSIZE(swap), 0); - 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; -} - //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]; @@ -351,92 +128,17 @@ static void DragonFlyBSDProcessList_updateProcessName(kvm_t* kd, const struct ki free(cmdline); } -static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) { - size_t len; - char* jls; /* Jail list */ - char* curpos; - char* nextpos; - - if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) { - CRT_fatalError("initial sysctlbyname / jail.list failed"); - } - -retry: - if (len == 0) - return; - - jls = xMalloc(len); - - if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) { - if (errno == ENOMEM) { - free(jls); - goto retry; - } - CRT_fatalError("sysctlbyname / jail.list failed"); - } - - if (dfpl->jails) { - Hashtable_delete(dfpl->jails); - } - - dfpl->jails = Hashtable_new(20, true); - curpos = jls; - while (curpos) { - int jailid; - char* str_hostname; - - nextpos = strchr(curpos, '\n'); - if (nextpos) { - *nextpos++ = 0; - } - - jailid = atoi(strtok(curpos, " ")); - str_hostname = strtok(NULL, " "); - - char* jname = (char*) (Hashtable_get(dfpl->jails, jailid)); - if (jname == NULL) { - jname = xStrdup(str_hostname); - Hashtable_put(dfpl->jails, jailid, jname); - } - - curpos = nextpos; - } - - free(jls); -} - -static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) { - char* hostname; - char* jname; - - if (jailid != 0 && dfpl->jails && (hostname = (char*)Hashtable_get(dfpl->jails, jailid))) { - jname = xStrdup(hostname); - } else { - jname = xStrdup("-"); - } - - return jname; -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) super; +void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; + const DragonFlyMachine* dhost = (const DragonFlyMachine*) host; const Settings* settings = host->settings; + bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; - DragonFlyBSDProcessList_scanMemoryInfo(super); - DragonFlyBSDProcessList_scanCPUTime(super); - DragonFlyBSDProcessList_scanJails(dfpl); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - int count = 0; - const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); + const struct kinfo_proc* kprocs = kvm_getprocs(dhost->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; @@ -480,7 +182,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } DragonFlyBSDProcessList_updateExe(kproc, proc); - DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc); + DragonFlyBSDProcessList_updateProcessName(dhost->kd, kproc, proc); if (settings->ss->flags & PROCESS_FLAG_CWD) { DragonFlyBSDProcessList_updateCwd(kproc, proc); @@ -488,13 +190,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { ProcessList_add(super, proc); - dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid); + dfp->jname = DragonFlyBSDMachine_readJailName(dhost, kproc->kp_jailid); } else { proc->processor = kproc->kp_lwp.kl_cpuid; if (dfp->jid != kproc->kp_jailid) { // process can enter jail anytime dfp->jid = kproc->kp_jailid; free(dfp->jname); - dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid); + dfp->jname = DragonFlyBSDMachine_readJailName(dhost, kproc->kp_jailid); } // if there are reapers in the system, process can get reparented anytime proc->ppid = kproc->kp_ppid; @@ -503,16 +205,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (settings->updateProcessNames) { - DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc); + DragonFlyBSDProcessList_updateProcessName(dhost->kd, kproc, proc); } } proc->m_virt = kproc->kp_vm_map_size / ONE_K; - proc->m_resident = kproc->kp_vm_rssize * pageSizeKb; + proc->m_resident = kproc->kp_vm_rssize * dhost->pageSizeKb; proc->nlwp = kproc->kp_nthreads; // number of lwp thread proc->time = (kproc->kp_lwp.kl_uticks + kproc->kp_lwp.kl_sticks + kproc->kp_lwp.kl_iticks) / 10000; - proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale); + proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)dhost->kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem); Process_updateCPUFieldWidths(proc->percent_cpu); @@ -605,22 +307,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->updated = true; } } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) host; (void) id; - - return true; -} diff --git a/dragonflybsd/DragonFlyBSDProcessList.h b/dragonflybsd/DragonFlyBSDProcessList.h index 406722e0..3f5cdc3e 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.h +++ b/dragonflybsd/DragonFlyBSDProcessList.h @@ -8,61 +8,14 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include // required for kvm.h -#include -#include #include -#include #include -#include -#include -#include "Hashtable.h" #include "ProcessList.h" -#include "UsersTable.h" -#include "dragonflybsd/DragonFlyBSDProcess.h" - - -typedef struct CPUData_ { - double userPercent; - double nicePercent; - double systemPercent; - double irqPercent; - double idlePercent; - double systemAllPercent; -} CPUData; typedef struct DragonFlyBSDProcessList_ { ProcessList super; - kvm_t* kd; - - unsigned long long int memWire; - unsigned long long int memActive; - unsigned long long int memInactive; - unsigned long long int memFree; - - CPUData* cpus; - - unsigned long* cp_time_o; - unsigned long* cp_time_n; - - unsigned long* cp_times_o; - unsigned long* cp_times_n; - - Hashtable* jails; } DragonFlyBSDProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 1c0ef0a4..36307e93 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -173,15 +173,15 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) host->pl; - unsigned int cpus = this->host->activeCPUs; + const DragonFlyBSDMachine* dhost = (const DragonFlyBSDMachine*) host; + unsigned int cpus = host->activeCPUs; const CPUData* cpuData; if (cpus == 1) { // single CPU box has everything in fpl->cpus[0] - cpuData = &(fpl->cpus[0]); + cpuData = &(dhost->cpus[0]); } else { - cpuData = &(fpl->cpus[cpu]); + cpuData = &(dhost->cpus[cpu]); } double percent; @@ -189,7 +189,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (host->settings->detailedCPUTime) { + if (super->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -209,22 +209,21 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - // TODO - const ProcessList* pl = this->host->pl; + const Machine* host = this->host; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" // mtr->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->host->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" // mtr->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } diff --git a/freebsd/FreeBSDMachine.c b/freebsd/FreeBSDMachine.c new file mode 100644 index 00000000..b8d5d87f --- /dev/null +++ b/freebsd/FreeBSDMachine.c @@ -0,0 +1,396 @@ +/* +htop - FreeBSDMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "freebsd/FreeBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include // needs to be included before for MAXPATHLEN +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Compat.h" +#include "Macros.h" +#include "Object.h" +#include "Scheduling.h" +#include "Settings.h" +#include "XUtils.h" +#include "generic/openzfs_sysctl.h" +#include "zfs/ZfsArcStats.h" + + +static int MIB_hw_physmem[2]; +static int MIB_vm_stats_vm_v_page_count[4]; + +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_vm_vmtotal[2]; + +static int MIB_vfs_bufspace[2]; + +static int MIB_kern_cp_time[2]; +static int MIB_kern_cp_times[2]; + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + FreeBSDMachine* this = xCalloc(1, sizeof(FreeBSDMachine)); + Machine* super = &this->super; + char errbuf[_POSIX2_LINE_MAX]; + size_t len; + + Machine_init(this, usersTable, userId); + + // 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(this->pageSize); + if (sysctlbyname("vm.stats.vm.v_page_size", &this->pageSize, &len, NULL, 0) == -1) + CRT_fatalError("Cannot get pagesize by sysctl"); + this->pageSizeKb = this->pageSize / ONE_K; + + // 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("vm.vmtotal", MIB_vm_vmtotal, &len); + + len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); + + openzfs_sysctl_init(&this->zfs); + openzfs_sysctl_updateArcStats(&this->zfs); + + int smp = 0; + len = sizeof(smp); + + if (sysctlbyname("kern.smp.active", &smp, &len, NULL, 0) != 0 || len != sizeof(smp)) { + smp = 0; + } + + int cpus = 1; + len = sizeof(cpus); + + if (smp) { + int err = sysctlbyname("kern.smp.cpus", &cpus, &len, NULL, 0); + if (err) { + cpus = 1; + } + } else { + cpus = 1; + } + + size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; + len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); + this->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); + this->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); + len = sizeof_cp_time_array; + + // fetch initial single (or average) CPU clicks from kernel + sysctl(MIB_kern_cp_time, 2, this->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); + this->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); + this->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); + len = cpus * sizeof_cp_time_array; + sysctl(MIB_kern_cp_times, 2, this->cp_times_o, &len, NULL, 0); + } + + super->existingCPUs = MAXIMUM(cpus, 1); + // TODO: support offline CPUs and hot swapping + super->activeCPUs = super->existingCPUs; + + if (cpus == 1 ) { + this->cpus = xRealloc(this->cpus, sizeof(CPUData)); + } else { + // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) + this->cpus = xRealloc(this->cpus, (super->existingCPUs + 1) * sizeof(CPUData)); + } + + len = sizeof(this->kernelFScale); + if (sysctlbyname("kern.fscale", &this->kernelFScale, &len, NULL, 0) == -1) { + //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed + this->kernelFScale = 2048; + } + + this->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return this; +} + +void Machine_delete(Machine* super) { + FreeBSDMachine* this = (FreeBSDMachine*) super; + + ProcessList_done(this); + + if (this->kd) { + kvm_close(this->kd); + } + + free(this->cp_time_o); + free(this->cp_time_n); + free(this->cp_times_o); + free(this->cp_times_n); + free(this->cpus); + + free(this); +} + +static inline void FreeBSDMachine_scanCPU(Machine* super) { + const FreeBSDMachine* this = (FreeBSDMachine*) super; + + unsigned int cpus = super->existingCPUs; // actual CPU count + unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) + int cp_times_offset; + + assert(cpus > 0); + + size_t sizeof_cp_time_array; + + unsigned long* cp_time_n; // old clicks state + unsigned long* cp_time_o; // current clicks state + + 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, this->cp_time_n, &sizeof_cp_time_array, NULL, 0); + + // get rest of CPUs + if (cpus > 1) { + // on smp systems FreeBSD kernel concats all CPU states into one long array in + // kern.cp_times sysctl OID + // we store averages in this->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, this->cp_times_n, &sizeof_cp_time_array, NULL, 0); + } + + for (unsigned int i = 0; i < maxcpu; i++) { + if (cpus == 1) { + // single CPU box + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + if (i == 0 ) { + // average + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + // specific smp cores + cp_times_offset = i - 1; + cp_time_n = this->cp_times_n + (cp_times_offset * CPUSTATES); + cp_time_o = this->cp_times_o + (cp_times_offset * CPUSTATES); + } + } + + // 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++) { + cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; + total_o += cp_time_o[s]; + total_n += cp_time_n[s]; + } + + // totals + total_d = total_n - total_o; + if (total_d < 1 ) { + total_d = 1; + } + + // save current state as old and calc percentages + for (int s = 0; s < CPUSTATES; ++s) { + cp_time_o[s] = cp_time_n[s]; + cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; + } + + CPUData* cpuData = &(this->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 + //cpuData->idlePercent = cp_time_p[CP_IDLE]; + + cpuData->temperature = NAN; + cpuData->frequency = NAN; + + const int coreId = (cpus == 1) ? 0 : ((int)i - 1); + if (coreId < 0) + continue; + + // TODO: test with hyperthreading and multi-cpu systems + if (super->settings->showCPUTemperature) { + int temperature; + size_t len = sizeof(temperature); + char mibBuffer[32]; + xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.temperature", coreId); + int r = sysctlbyname(mibBuffer, &temperature, &len, NULL, 0); + if (r == 0) + cpuData->temperature = (double)(temperature - 2732) / 10.0; // convert from deci-Kelvin to Celsius + } + + // TODO: test with hyperthreading and multi-cpu systems + if (super->settings->showCPUFrequency) { + int frequency; + size_t len = sizeof(frequency); + char mibBuffer[32]; + xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.freq", coreId); + int r = sysctlbyname(mibBuffer, &frequency, &len, NULL, 0); + if (r == 0) + cpuData->frequency = frequency; // keep in MHz + } + } + + // calculate max temperature and avg frequency for average meter and + // propagate frequency to all cores if only supplied for CPU 0 + if (cpus > 1) { + if (super->settings->showCPUTemperature) { + double maxTemp = NAN; + for (unsigned int i = 1; i < maxcpu; i++) { + const double coreTemp = this->cpus[i].temperature; + if (isnan(coreTemp)) + continue; + + maxTemp = MAXIMUM(maxTemp, coreTemp); + } + + this->cpus[0].temperature = maxTemp; + } + + if (super->settings->showCPUFrequency) { + const double coreZeroFreq = this->cpus[1].frequency; + double freqSum = coreZeroFreq; + if (!isnan(coreZeroFreq)) { + for (unsigned int i = 2; i < maxcpu; i++) { + if (isnan(this->cpus[i].frequency)) + this->cpus[i].frequency = coreZeroFreq; + + freqSum += this->cpus[i].frequency; + } + + this->cpus[0].frequency = freqSum / (maxcpu - 1); + } + } + } +} + +static void FreeBSDMachine_scanMemoryInfo(Machine* super) { + FreeBSDMachine* this = (FreeBSDMachine*) super; + + // @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 + // + // with ZFS ARC situation becomes bit muddled, as ARC behaves like "user_free" + // and belongs into cache, but is reported as wired by kernel + // + // htop_used = active + (wired - arc) + // htop_cache = buffers + cache + arc + u_long totalMem; + u_int memActive, memWire, cachedMem; + long buffersMem; + size_t len; + struct vmtotal vmtotal; + + //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, &(super->totalMem), &len, NULL, 0); + //super->totalMem *= this->pageSizeKb; + len = sizeof(totalMem); + sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); + totalMem /= 1024; + super->totalMem = totalMem; + + len = sizeof(memActive); + sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); + memActive *= this->pageSizeKb; + this->memActive = memActive; + + len = sizeof(memWire); + sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); + memWire *= this->pageSizeKb; + this->memWire = memWire; + + len = sizeof(buffersMem); + sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); + buffersMem /= 1024; + super->buffersMem = buffersMem; + + len = sizeof(cachedMem); + sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); + cachedMem *= this->pageSizeKb; + super->cachedMem = cachedMem; + + len = sizeof(vmtotal); + sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); + super->sharedMem = vmtotal.t_rmshr * this->pageSizeKb; + + super->usedMem = this->memActive + this->memWire; + + struct kvm_swap swap[16]; + int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0); + super->totalSwap = 0; + super->usedSwap = 0; + for (int i = 0; i < nswap; i++) { + super->totalSwap += swap[i].ksw_total; + super->usedSwap += swap[i].ksw_used; + } + super->totalSwap *= this->pageSizeKb; + super->usedSwap *= this->pageSizeKb; +} + +void Machine_scan(Machine* super) { + FreeBSDMachine* this = (FreeBSDMachine*) super; + + openzfs_sysctl_updateArcStats(&this->zfs); + FreeBSDMachine_scanMemoryInfo(super); + FreeBSDMachine_scanCPU(super); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) host; (void) id; + + return true; +} diff --git a/freebsd/FreeBSDMachine.h b/freebsd/FreeBSDMachine.h new file mode 100644 index 00000000..1a90534e --- /dev/null +++ b/freebsd/FreeBSDMachine.h @@ -0,0 +1,54 @@ +#ifndef HEADER_FreeBSDMachine +#define HEADER_FreeBSDMachine +/* +htop - FreeBSDMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "UsersTable.h" +#include "zfs/ZfsArcStats.h" + + +typedef struct CPUData_ { + double userPercent; + double nicePercent; + double systemPercent; + double irqPercent; + double systemAllPercent; + + double frequency; + double temperature; +} CPUData; + +typedef struct FreeBSDProcessList_ { + Machine super; + kvm_t* kd; + + int pageSize; + int pageSizeKb; + int kernelFScale; + + unsigned long long int memWire; + unsigned long long int memActive; + + ZfsArcStats zfs; + + CPUData* cpus; + + unsigned long* cp_time_o; + unsigned long* cp_time_n; + + unsigned long* cp_times_o; + unsigned long* cp_times_n; + +} FreeBSDProcessList; + +#endif diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index bd6e3aac..1cf32502 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -37,354 +37,25 @@ in the source distribution for its full text. #include "Scheduling.h" #include "Settings.h" #include "XUtils.h" -#include "generic/openzfs_sysctl.h" -#include "zfs/ZfsArcStats.h" -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_vm_vmtotal[2]; - -static int MIB_vfs_bufspace[2]; - -static int MIB_kern_cp_time[2]; -static int MIB_kern_cp_times[2]; -static int kernelFScale; - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - size_t len; - char errbuf[_POSIX2_LINE_MAX]; - FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); - ProcessList* pl = (ProcessList*) fpl; - ProcessList_init(pl, Class(FreeBSDProcess), host, pidMatchList); - - // 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); - if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) - CRT_fatalError("Cannot get pagesize by sysctl"); - pageSizeKb = pageSize / ONE_K; - - // 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("vm.vmtotal", MIB_vm_vmtotal, &len); - - len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); - - openzfs_sysctl_init(&fpl->zfs); - openzfs_sysctl_updateArcStats(&fpl->zfs); - - int smp = 0; - len = sizeof(smp); - - if (sysctlbyname("kern.smp.active", &smp, &len, NULL, 0) != 0 || len != sizeof(smp)) { - smp = 0; - } + FreeBSDProcessList* this = xCalloc(1, sizeof(FreeBSDProcessList)); + ProcessList* super = &this->super; - int cpus = 1; - len = sizeof(cpus); - - if (smp) { - int err = sysctlbyname("kern.smp.cpus", &cpus, &len, NULL, 0); - if (err) { - cpus = 1; - } - } else { - cpus = 1; - } - - size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; - len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); - fpl->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); - fpl->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); - len = sizeof_cp_time_array; - - // fetch initial single (or average) CPU clicks from kernel - sysctl(MIB_kern_cp_time, 2, fpl->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); - fpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); - fpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); - len = cpus * sizeof_cp_time_array; - sysctl(MIB_kern_cp_times, 2, fpl->cp_times_o, &len, NULL, 0); - } + ProcessList_init(super, Class(FreeBSDProcess), host, pidMatchList); - host->existingCPUs = MAXIMUM(cpus, 1); - // TODO: support offline CPUs and hot swapping - host->activeCPUs = host->existingCPUs; - - if (cpus == 1 ) { - fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData)); - } else { - // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - fpl->cpus = xRealloc(fpl->cpus, (host->existingCPUs + 1) * sizeof(CPUData)); - } - - - 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; - } - - fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); - if (fpl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - return pl; + return this; } -void ProcessList_delete(ProcessList* this) { - const FreeBSDProcessList* fpl = (FreeBSDProcessList*) this; +void ProcessList_delete(ProcessList* super) { + FreeBSDProcessList* this = (FreeBSDProcessList*) super; - if (fpl->kd) { - kvm_close(fpl->kd); - } - - free(fpl->cp_time_o); - free(fpl->cp_time_n); - free(fpl->cp_times_o); - free(fpl->cp_times_n); - free(fpl->cpus); + ProcessList_done(super); - ProcessList_done(this); free(this); } -static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { - const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; - const Machine* host = pl->host; - - unsigned int cpus = host->existingCPUs; // actual CPU count - unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) - int cp_times_offset; - - assert(cpus > 0); - - size_t sizeof_cp_time_array; - - unsigned long* cp_time_n; // old clicks state - unsigned long* cp_time_o; // current clicks state - - 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, fpl->cp_time_n, &sizeof_cp_time_array, NULL, 0); - - // get rest of CPUs - if (cpus > 1) { - // on smp systems FreeBSD kernel concats all CPU states into one long array in - // kern.cp_times sysctl OID - // we store averages in fpl->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, fpl->cp_times_n, &sizeof_cp_time_array, NULL, 0); - } - - for (unsigned int i = 0; i < maxcpu; i++) { - if (cpus == 1) { - // single CPU box - cp_time_n = fpl->cp_time_n; - cp_time_o = fpl->cp_time_o; - } else { - if (i == 0 ) { - // average - cp_time_n = fpl->cp_time_n; - cp_time_o = fpl->cp_time_o; - } else { - // specific smp cores - cp_times_offset = i - 1; - cp_time_n = fpl->cp_times_n + (cp_times_offset * CPUSTATES); - cp_time_o = fpl->cp_times_o + (cp_times_offset * CPUSTATES); - } - } - - // 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++) { - cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; - total_o += cp_time_o[s]; - total_n += cp_time_n[s]; - } - - // totals - total_d = total_n - total_o; - if (total_d < 1 ) { - total_d = 1; - } - - // save current state as old and calc percentages - for (int s = 0; s < CPUSTATES; ++s) { - cp_time_o[s] = cp_time_n[s]; - cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; - } - - CPUData* cpuData = &(fpl->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 - //cpuData->idlePercent = cp_time_p[CP_IDLE]; - - cpuData->temperature = NAN; - cpuData->frequency = NAN; - - const int coreId = (cpus == 1) ? 0 : ((int)i - 1); - if (coreId < 0) - continue; - - // TODO: test with hyperthreading and multi-cpu systems - if (host->settings->showCPUTemperature) { - int temperature; - size_t len = sizeof(temperature); - char mibBuffer[32]; - xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.temperature", coreId); - int r = sysctlbyname(mibBuffer, &temperature, &len, NULL, 0); - if (r == 0) - cpuData->temperature = (double)(temperature - 2732) / 10.0; // convert from deci-Kelvin to Celsius - } - - // TODO: test with hyperthreading and multi-cpu systems - if (host->settings->showCPUFrequency) { - int frequency; - size_t len = sizeof(frequency); - char mibBuffer[32]; - xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.freq", coreId); - int r = sysctlbyname(mibBuffer, &frequency, &len, NULL, 0); - if (r == 0) - cpuData->frequency = frequency; // keep in MHz - } - } - - // calculate max temperature and avg frequency for average meter and - // propagate frequency to all cores if only supplied for CPU 0 - if (cpus > 1) { - if (host->settings->showCPUTemperature) { - double maxTemp = NAN; - for (unsigned int i = 1; i < maxcpu; i++) { - const double coreTemp = fpl->cpus[i].temperature; - if (isnan(coreTemp)) - continue; - - maxTemp = MAXIMUM(maxTemp, coreTemp); - } - - fpl->cpus[0].temperature = maxTemp; - } - - if (host->settings->showCPUFrequency) { - const double coreZeroFreq = fpl->cpus[1].frequency; - double freqSum = coreZeroFreq; - if (!isnan(coreZeroFreq)) { - for (unsigned int i = 2; i < maxcpu; i++) { - if (isnan(fpl->cpus[i].frequency)) - fpl->cpus[i].frequency = coreZeroFreq; - - freqSum += fpl->cpus[i].frequency; - } - - fpl->cpus[0].frequency = freqSum / (maxcpu - 1); - } - } - } -} - -static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { - FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; - Machine* host = pl->host; - - // @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 - // - // with ZFS ARC situation becomes bit muddled, as ARC behaves like "user_free" - // and belongs into cache, but is reported as wired by kernel - // - // htop_used = active + (wired - arc) - // htop_cache = buffers + cache + arc - u_long totalMem; - u_int memActive, memWire, cachedMem; - long buffersMem; - size_t len; - struct vmtotal vmtotal; - - //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, &(host->totalMem), &len, NULL, 0); - //host->totalMem *= pageSizeKb; - len = sizeof(totalMem); - sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); - totalMem /= 1024; - host->totalMem = totalMem; - - len = sizeof(memActive); - sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); - memActive *= pageSizeKb; - fpl->memActive = memActive; - - len = sizeof(memWire); - sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); - memWire *= pageSizeKb; - fpl->memWire = memWire; - - len = sizeof(buffersMem); - sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); - buffersMem /= 1024; - host->buffersMem = buffersMem; - - len = sizeof(cachedMem); - sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); - cachedMem *= pageSizeKb; - host->cachedMem = cachedMem; - - len = sizeof(vmtotal); - sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); - host->sharedMem = vmtotal.t_rmshr * pageSizeKb; - - host->usedMem = fpl->memActive + fpl->memWire; - - struct kvm_swap swap[16]; - int nswap = kvm_getswapinfo(fpl->kd, swap, ARRAYSIZE(swap), 0); - host->totalSwap = 0; - host->usedSwap = 0; - for (int i = 0; i < nswap; i++) { - host->totalSwap += swap[i].ksw_total; - host->usedSwap += swap[i].ksw_used; - } - host->totalSwap *= pageSizeKb; - host->usedSwap *= pageSizeKb; -} - static void FreeBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) { if (Process_isKernelThread(proc)) { Process_updateExe(proc, NULL); @@ -483,22 +154,14 @@ IGNORE_WCASTQUAL_END return NULL; } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; +void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; + const FreeBSDMachine* fhost = (FreeBSDMachine*) host; + FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; - openzfs_sysctl_updateArcStats(&fpl->zfs); - FreeBSDProcessList_scanMemoryInfo(super); - FreeBSDProcessList_scanCPU(super); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - int count = 0; const struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count); @@ -521,14 +184,14 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->st_uid = kproc->ki_uid; proc->starttime_ctime = kproc->ki_start.tv_sec; if (proc->starttime_ctime < 0) { - proc->starttime_ctime = super->host.realtimeMs / 1000; + proc->starttime_ctime = host->realtimeMs / 1000; } Process_fillStarttimeBuffer(proc); proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); ProcessList_add(super, proc); FreeBSDProcessList_updateExe(kproc, proc); - FreeBSDProcessList_updateProcessName(fpl->kd, kproc, proc); + FreeBSDProcessList_updateProcessName(fhost->kd, kproc, proc); if (settings->ss->flags & PROCESS_FLAG_CWD) { FreeBSDProcessList_updateCwd(kproc, proc); @@ -559,7 +222,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (settings->updateProcessNames) { - FreeBSDProcessList_updateProcessName(fpl->kd, kproc, proc); + FreeBSDProcessList_updateProcessName(fhost->kd, kproc, proc); } } @@ -567,11 +230,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { // from FreeBSD source /src/usr.bin/top/machine.c proc->m_virt = kproc->ki_size / ONE_K; - proc->m_resident = kproc->ki_rssize * pageSizeKb; + proc->m_resident = kproc->ki_rssize * fhost->pageSizeKb; proc->nlwp = kproc->ki_numthreads; proc->time = (kproc->ki_runtime + 5000) / 10000; - proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); + proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)fhost->kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(host->totalMem); Process_updateCPUFieldWidths(proc->percent_cpu); @@ -623,22 +286,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->updated = true; } } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) host; (void) id; - - return true; -} diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h index b7e29f8b..55247eb7 100644 --- a/freebsd/FreeBSDProcessList.h +++ b/freebsd/FreeBSDProcessList.h @@ -7,56 +7,15 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" -#include "zfs/ZfsArcStats.h" - - -typedef struct CPUData_ { - double userPercent; - double nicePercent; - double systemPercent; - double irqPercent; - double systemAllPercent; - - double frequency; - double temperature; -} CPUData; typedef struct FreeBSDProcessList_ { ProcessList super; - kvm_t* kd; - - unsigned long long int memWire; - unsigned long long int memActive; - - ZfsArcStats zfs; - - CPUData* cpus; - - unsigned long* cp_time_o; - unsigned long* cp_time_n; - - unsigned long* cp_times_o; - unsigned long* cp_times_n; - } FreeBSDProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 3ba2778e..0588b0fa 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -34,20 +34,19 @@ in the source distribution for its full text. #include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" +#include "Machine.h" #include "Macros.h" #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "Meter.h" #include "NetworkIOMeter.h" -#include "ProcessList.h" #include "Settings.h" #include "SwapMeter.h" #include "SysArchMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" -#include "freebsd/FreeBSDProcess.h" -#include "freebsd/FreeBSDProcessList.h" +#include "freebsd/FreeBSDMachine.h" #include "generic/fdstat_sysctl.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" @@ -194,15 +193,15 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) host->pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; unsigned int cpus = host->activeCPUs; const CPUData* cpuData; if (cpus == 1) { - // single CPU box has everything in fpl->cpus[0] - cpuData = &(fpl->cpus[0]); + // single CPU box has everything in fhost->cpus[0] + cpuData = &(fhost->cpus[0]); } else { - cpuData = &(fpl->cpus[cpu]); + cpuData = &(fhost->cpus[cpu]); } double percent; @@ -210,7 +209,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (host->settings->detailedCPUTime) { + if (super->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -231,8 +230,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - const ProcessList* pl = host->pl; - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; this->total = host->totalMem; this->values[MEMORY_METER_USED] = host->usedMem; @@ -242,11 +240,11 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" - if (fpl->zfs.enabled) { + if (dhost->zfs.enabled) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (fpl->zfs.size > fpl->zfs.min) - shrinkableSize = fpl->zfs.size - fpl->zfs.min; + if (dhost->zfs.size > dhost->zfs.min) + shrinkableSize = dhost->zfs.size - dhost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; @@ -263,15 +261,15 @@ void Platform_setSwapValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->host->pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) this->host; - ZfsArcMeter_readStats(this, &(fpl->zfs)); + ZfsArcMeter_readStats(this, &fhost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->host->pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(fpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &fhost->zfs); } char* Platform_getProcessEnv(pid_t pid) { diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c index 0e429444..ec3804ee 100644 --- a/linux/HugePageMeter.c +++ b/linux/HugePageMeter.c @@ -16,7 +16,7 @@ in the source distribution for its full text. #include "Object.h" #include "ProcessList.h" #include "RichString.h" -#include "linux/LinuxProcessList.h" +#include "linux/LinuxMachine.h" static const char* HugePageMeter_active_labels[4] = { NULL, NULL, NULL, NULL }; @@ -43,8 +43,8 @@ static void HugePageMeter_updateValues(Meter* this) { memory_t usedTotal = 0; unsigned nextUsed = 0; - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; - this->total = lpl->totalHugePageMem; + const LinuxMachine* host = (const LinuxMachine*) this->host; + this->total = host->totalHugePageMem; this->values[0] = 0; HugePageMeter_active_labels[0] = " used:"; for (unsigned i = 1; i < ARRAYSIZE(HugePageMeter_active_labels); i++) { @@ -52,7 +52,7 @@ static void HugePageMeter_updateValues(Meter* this) { HugePageMeter_active_labels[i] = NULL; } for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { - memory_t value = lpl->usedHugePageMem[i]; + memory_t value = host->usedHugePageMem[i]; if (value != MEMORY_MAX) { this->values[nextUsed] = value; usedTotal += value; diff --git a/linux/LibSensors.c b/linux/LibSensors.c index 9a27fe57..ff084b64 100644 --- a/linux/LibSensors.c +++ b/linux/LibSensors.c @@ -16,7 +16,7 @@ #include "Macros.h" #include "XUtils.h" -#include "linux/LinuxProcessList.h" +#include "linux/LinuxMachine.h" #ifdef BUILD_STATIC diff --git a/linux/LibSensors.h b/linux/LibSensors.h index aa899793..2b9801bc 100644 --- a/linux/LibSensors.h +++ b/linux/LibSensors.h @@ -1,7 +1,7 @@ #ifndef HEADER_LibSensors #define HEADER_LibSensors -#include "linux/LinuxProcessList.h" +#include "linux/LinuxMachine.h" int LibSensors_init(void); diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c new file mode 100644 index 00000000..48d0e80e --- /dev/null +++ b/linux/LinuxMachine.c @@ -0,0 +1,707 @@ +/* +htop - LinuxMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "linux/LinuxMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Compat.h" +#include "XUtils.h" +#include "linux/LinuxMachine.h" +#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep + +#ifdef HAVE_SENSORS_SENSORS_H +#include "LibSensors.h" +#endif + +#ifndef O_PATH +#define O_PATH 010000000 // declare for ancient glibc versions +#endif + +/* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF + * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD + */ +static void LinuxMachine_updateCPUcount(LinuxMachine* this) { + unsigned int existing = 0, active = 0; + Machine* super = &this->super; + + // Initialize the cpuData array before anything else. + if (!this->cpuData) { + this->cpuData = xCalloc(2, sizeof(CPUData)); + this->cpuData[0].online = true; /* average is always "online" */ + this->cpuData[1].online = true; + super->activeCPUs = 1; + super->existingCPUs = 1; + } + + DIR* dir = opendir("/sys/devices/system/cpu"); + if (!dir) + return; + + unsigned int currExisting = super->existingCPUs; + + const struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) + continue; + + if (!String_startsWith(entry->d_name, "cpu")) + continue; + + char* endp; + unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); + if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') + continue; + +#ifdef HAVE_OPENAT + int cpuDirFd = openat(dirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW); + if (cpuDirFd < 0) + continue; +#else + char cpuDirFd[4096]; + xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name); +#endif + + existing++; + + /* readdir() iterates with no specific order */ + unsigned int max = MAXIMUM(existing, id + 1); + if (max > currExisting) { + this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData)); + this->cpuData[0].online = true; /* average is always "online" */ + currExisting = max; + } + + char buffer[8]; + ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer)); + /* If the file "online" does not exist or on failure count as active */ + if (res < 1 || buffer[0] != '0') { + active++; + this->cpuData[id + 1].online = true; + } else { + this->cpuData[id + 1].online = false; + } + + Compat_openatArgClose(cpuDirFd); + } + + closedir(dir); + + // return if no CPU is found + if (existing < 1) + return; + +#ifdef HAVE_SENSORS_SENSORS_H + /* When started with offline CPUs, libsensors does not monitor those, + * even when they become online. */ + if (super->existingCPUs != 0 && (active > super->activeCPUs || currExisting > super->existingCPUs)) + LibSensors_reload(); +#endif + + super->activeCPUs = active; + assert(existing == currExisting); + super->existingCPUs = currExisting; +} + +static void LinuxMachine_scanMemoryInfo(LinuxMachine* this) { + Machine* host = &this->super; + memory_t availableMem = 0; + memory_t freeMem = 0; + memory_t totalMem = 0; + memory_t buffersMem = 0; + memory_t cachedMem = 0; + memory_t sharedMem = 0; + memory_t swapTotalMem = 0; + memory_t swapCacheMem = 0; + memory_t swapFreeMem = 0; + memory_t sreclaimableMem = 0; + memory_t zswapCompMem = 0; + memory_t zswapOrigMem = 0; + + FILE* file = fopen(PROCMEMINFOFILE, "r"); + if (!file) + CRT_fatalError("Cannot open " PROCMEMINFOFILE); + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), file)) { + + #define tryRead(label, variable) \ + if (String_startsWith(buffer, label)) { \ + memory_t parsed_; \ + if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \ + (variable) = parsed_; \ + } \ + break; \ + } else (void) 0 /* Require a ";" after the macro use. */ + + switch (buffer[0]) { + case 'M': + tryRead("MemAvailable:", availableMem); + tryRead("MemFree:", freeMem); + tryRead("MemTotal:", totalMem); + break; + case 'B': + tryRead("Buffers:", buffersMem); + break; + case 'C': + tryRead("Cached:", cachedMem); + break; + case 'S': + switch (buffer[1]) { + case 'h': + tryRead("Shmem:", sharedMem); + break; + case 'w': + tryRead("SwapTotal:", swapTotalMem); + tryRead("SwapCached:", swapCacheMem); + tryRead("SwapFree:", swapFreeMem); + break; + case 'R': + tryRead("SReclaimable:", sreclaimableMem); + break; + } + break; + case 'Z': + tryRead("Zswap:", zswapCompMem); + tryRead("Zswapped:", zswapOrigMem); + break; + } + + #undef tryRead + } + + fclose(file); + + /* + * Compute memory partition like procps(free) + * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c + * + * Adjustments: + * - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/), + * do not show twice by subtracting from Cached and do not subtract twice from used. + */ + host->totalMem = totalMem; + host->cachedMem = cachedMem + sreclaimableMem - sharedMem; + host->sharedMem = sharedMem; + const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; + host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; + host->buffersMem = buffersMem; + host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; + host->totalSwap = swapTotalMem; + host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; + host->cachedSwap = swapCacheMem; + this->zswap.usedZswapComp = zswapCompMem; + this->zswap.usedZswapOrig = zswapOrigMem; +} + +static void LinuxMachine_scanHugePages(LinuxMachine* this) { + this->totalHugePageMem = 0; + for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { + this->usedHugePageMem[i] = MEMORY_MAX; + } + + DIR* dir = opendir("/sys/kernel/mm/hugepages"); + if (!dir) + return; + + const struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + const char* name = entry->d_name; + + /* Ignore all non-directories */ + if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) + continue; + + if (!String_startsWith(name, "hugepages-")) + continue; + + char* endptr; + unsigned long int hugePageSize = strtoul(name + strlen("hugepages-"), &endptr, 10); + if (!endptr || *endptr != 'k') + continue; + + char content[64]; + char hugePagePath[128]; + ssize_t r; + + xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/nr_hugepages", name); + r = xReadfile(hugePagePath, content, sizeof(content)); + if (r <= 0) + continue; + + memory_t total = strtoull(content, NULL, 10); + if (total == 0) + continue; + + xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/free_hugepages", name); + r = xReadfile(hugePagePath, content, sizeof(content)); + if (r <= 0) + continue; + + memory_t free = strtoull(content, NULL, 10); + + int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10); + assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT); + + this->totalHugePageMem += total * hugePageSize; + this->usedHugePageMem[shift] = (total - free) * hugePageSize; + } + + closedir(dir); +} + +static inline void LinuxMachine_scanZswapInfo(LinuxMachine* this) { + const Machine* host = &this->super; + long max_pool_percent = 0; + char buf[256]; + int r; + + r = xReadfile("/sys/module/zswap/parameters/max_pool_percent", buf, 256); + if (r <= 0) { + return; + } + max_pool_percent = strtol(buf, NULL, 10); + if (max_pool_percent < 0 || max_pool_percent > 100) { + return; + } + + this->zswap.totalZswapPool = host->totalMem * max_pool_percent / 100; + /* the rest of the metrics are set in LinuxMachine_scanMemoryInfo() */ +} + +static void LinuxMachine_scanZramInfo(LinuxMachine* this) { + memory_t totalZram = 0; + memory_t usedZramComp = 0; + memory_t usedZramOrig = 0; + + char mm_stat[34]; + char disksize[34]; + + unsigned int i = 0; + for (;;) { + xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i); + xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i); + i++; + FILE* disksize_file = fopen(disksize, "r"); + FILE* mm_stat_file = fopen(mm_stat, "r"); + if (disksize_file == NULL || mm_stat_file == NULL) { + if (disksize_file) { + fclose(disksize_file); + } + if (mm_stat_file) { + fclose(mm_stat_file); + } + break; + } + memory_t size = 0; + memory_t orig_data_size = 0; + memory_t compr_data_size = 0; + + if (!fscanf(disksize_file, "%llu\n", &size) || + !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) { + fclose(disksize_file); + fclose(mm_stat_file); + break; + } + + totalZram += size; + usedZramComp += compr_data_size; + usedZramOrig += orig_data_size; + + fclose(disksize_file); + fclose(mm_stat_file); + } + + this->zram.totalZram = totalZram / 1024; + this->zram.usedZramComp = usedZramComp / 1024; + this->zram.usedZramOrig = usedZramOrig / 1024; +} + +static void LinuxMachine_scanZfsArcstats(LinuxMachine* this) { + memory_t dbufSize = 0; + memory_t dnodeSize = 0; + memory_t bonusSize = 0; + + FILE* file = fopen(PROCARCSTATSFILE, "r"); + if (file == NULL) { + this->zfs.enabled = 0; + return; + } + char buffer[128]; + while (fgets(buffer, 128, file)) { + #define tryRead(label, variable) \ + if (String_startsWith(buffer, label)) { \ + sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ + break; \ + } else (void) 0 /* Require a ";" after the macro use. */ + #define tryReadFlag(label, variable, flag) \ + if (String_startsWith(buffer, label)) { \ + (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ + break; \ + } else (void) 0 /* Require a ";" after the macro use. */ + + switch (buffer[0]) { + case 'c': + tryRead("c_min", &this->zfs.min); + tryRead("c_max", &this->zfs.max); + tryReadFlag("compressed_size", &this->zfs.compressed, this->zfs.isCompressed); + break; + case 'u': + tryRead("uncompressed_size", &this->zfs.uncompressed); + break; + case 's': + tryRead("size", &this->zfs.size); + break; + case 'h': + tryRead("hdr_size", &this->zfs.header); + break; + case 'd': + tryRead("dbuf_size", &dbufSize); + tryRead("dnode_size", &dnodeSize); + break; + case 'b': + tryRead("bonus_size", &bonusSize); + break; + case 'a': + tryRead("anon_size", &this->zfs.anon); + break; + case 'm': + tryRead("mfu_size", &this->zfs.MFU); + tryRead("mru_size", &this->zfs.MRU); + break; + } + #undef tryRead + #undef tryReadFlag + } + fclose(file); + + this->zfs.enabled = (this->zfs.size > 0 ? 1 : 0); + this->zfs.size /= 1024; + this->zfs.min /= 1024; + this->zfs.max /= 1024; + this->zfs.MFU /= 1024; + this->zfs.MRU /= 1024; + this->zfs.anon /= 1024; + this->zfs.header /= 1024; + this->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024; + if ( this->zfs.isCompressed ) { + this->zfs.compressed /= 1024; + this->zfs.uncompressed /= 1024; + } +} + +static void LinuxMachine_scanCPUTime(LinuxMachine* this) { + const Machine* super = &this->super; + + LinuxMachine_updateCPUcount(this); + + FILE* file = fopen(PROCSTATFILE, "r"); + if (!file) + CRT_fatalError("Cannot open " PROCSTATFILE); + + unsigned int lastAdjCpuId = 0; + + for (unsigned int i = 0; i <= super->existingCPUs; i++) { + char buffer[PROC_LINE_LENGTH + 1]; + unsigned long long int usertime, nicetime, systemtime, idletime; + unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0; + + const char* ok = fgets(buffer, sizeof(buffer), file); + if (!ok) + break; + + // cpu fields are sorted first + if (!String_startsWith(buffer, "cpu")) + break; + + // Depending on your kernel version, + // 5, 7, 8 or 9 of these fields will be set. + // The rest will remain at zero. + unsigned int adjCpuId; + if (i == 0) { + (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + adjCpuId = 0; + } else { + unsigned int cpuid; + (void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + adjCpuId = cpuid + 1; + } + + if (adjCpuId > super->existingCPUs) + break; + + for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) { + // Skipped an ID, but /proc/stat is ordered => got offline CPU + memset(&(this->cpuData[j]), '\0', sizeof(CPUData)); + } + lastAdjCpuId = adjCpuId; + + // Guest time is already accounted in usertime + usertime -= guest; + nicetime -= guestnice; + // Fields existing on kernels >= 2.6 + // (and RHEL's patched kernel 2.4...) + unsigned long long int idlealltime = idletime + ioWait; + unsigned long long int systemalltime = systemtime + irq + softIrq; + unsigned long long int virtalltime = guest + guestnice; + unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; + CPUData* cpuData = &(this->cpuData[adjCpuId]); + // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() + // used in /proc/stat rounds down numbers, it can lead to a case where the + // integer overflow. + cpuData->userPeriod = saturatingSub(usertime, cpuData->userTime); + cpuData->nicePeriod = saturatingSub(nicetime, cpuData->niceTime); + cpuData->systemPeriod = saturatingSub(systemtime, cpuData->systemTime); + cpuData->systemAllPeriod = saturatingSub(systemalltime, cpuData->systemAllTime); + cpuData->idleAllPeriod = saturatingSub(idlealltime, cpuData->idleAllTime); + cpuData->idlePeriod = saturatingSub(idletime, cpuData->idleTime); + cpuData->ioWaitPeriod = saturatingSub(ioWait, cpuData->ioWaitTime); + cpuData->irqPeriod = saturatingSub(irq, cpuData->irqTime); + cpuData->softIrqPeriod = saturatingSub(softIrq, cpuData->softIrqTime); + cpuData->stealPeriod = saturatingSub(steal, cpuData->stealTime); + cpuData->guestPeriod = saturatingSub(virtalltime, cpuData->guestTime); + cpuData->totalPeriod = saturatingSub(totaltime, cpuData->totalTime); + cpuData->userTime = usertime; + cpuData->niceTime = nicetime; + cpuData->systemTime = systemtime; + cpuData->systemAllTime = systemalltime; + cpuData->idleAllTime = idlealltime; + cpuData->idleTime = idletime; + cpuData->ioWaitTime = ioWait; + cpuData->irqTime = irq; + cpuData->softIrqTime = softIrq; + cpuData->stealTime = steal; + cpuData->guestTime = virtalltime; + cpuData->totalTime = totaltime; + } + + this->period = (double)this->cpuData[0].totalPeriod / super->activeCPUs; + + char buffer[PROC_LINE_LENGTH + 1]; + while (fgets(buffer, sizeof(buffer), file)) { + if (String_startsWith(buffer, "procs_running")) { + super->pl->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); + break; + } + } + + fclose(file); +} + +static int scanCPUFrequencyFromSysCPUFreq(LinuxMachine* this) { + const Machine* super = &this->super; + int numCPUsWithFrequency = 0; + unsigned long totalFrequency = 0; + + /* + * On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow (> 1ms). This delay + * accumulates for every core. For details see issue#471. + * If the read on CPU 0 takes longer than 500us bail out and fall back to reading the + * frequencies from /proc/cpuinfo. + * Once the condition has been met, bail out early for the next couple of scans. + */ + static int timeout = 0; + + if (timeout > 0) { + timeout--; + return -1; + } + + for (unsigned int i = 0; i < super->existingCPUs; ++i) { + if (!Machine_isCPUonline(super, i)) + continue; + + char pathBuffer[64]; + xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); + + struct timespec start; + if (i == 0) + clock_gettime(CLOCK_MONOTONIC, &start); + + FILE* file = fopen(pathBuffer, "r"); + if (!file) + return -errno; + + unsigned long frequency; + if (fscanf(file, "%lu", &frequency) == 1) { + /* convert kHz to MHz */ + frequency = frequency / 1000; + this->cpuData[i + 1].frequency = frequency; + numCPUsWithFrequency++; + totalFrequency += frequency; + } + + fclose(file); + + if (i == 0) { + struct timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + const time_t timeTakenUs = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000; + if (timeTakenUs > 500) { + timeout = 30; + return -1; + } + } + } + + if (numCPUsWithFrequency > 0) + this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency; + + return 0; +} + +static void scanCPUFrequencyFromCPUinfo(LinuxMachine* this) { + const Machine* super = &this->super; + + FILE* file = fopen(PROCCPUINFOFILE, "r"); + if (file == NULL) + return; + + int numCPUsWithFrequency = 0; + double totalFrequency = 0; + int cpuid = -1; + + while (!feof(file)) { + double frequency; + char buffer[PROC_LINE_LENGTH]; + + if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) + break; + + if (sscanf(buffer, "processor : %d", &cpuid) == 1) { + continue; + } else if ( + (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || + (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) + ) { + if (cpuid < 0 || (unsigned int)cpuid > (super->existingCPUs - 1)) { + continue; + } + + CPUData* cpuData = &(this->cpuData[cpuid + 1]); + /* do not override sysfs data */ + if (isnan(cpuData->frequency)) { + cpuData->frequency = frequency; + } + numCPUsWithFrequency++; + totalFrequency += frequency; + } else if (buffer[0] == '\n') { + cpuid = -1; + } + } + fclose(file); + + if (numCPUsWithFrequency > 0) { + this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency; + } +} + +static void LinuxMachine_scanCPUFrequency(LinuxMachine* this) { + const Machine* super = &this->super; + + for (unsigned int i = 0; i <= super->existingCPUs; i++) + this->cpuData[i].frequency = NAN; + + if (scanCPUFrequencyFromSysCPUFreq(this) == 0) + return; + + scanCPUFrequencyFromCPUinfo(this); +} + +void Machine_scan(Machine* super) { + LinuxMachine* this = (LinuxMachine*) super; + + LinuxMachine_scanMemoryInfo(this); + LinuxMachine_scanHugePages(this); + LinuxMachine_scanZfsArcstats(this); + LinuxMachine_scanZramInfo(this); + LinuxMachine_scanZswapInfo(this); + LinuxMachine_scanCPUTime(this); + + const Settings* settings = super->settings; + if (settings->showCPUFrequency) + LinuxMachine_scanCPUFrequency(this); + + #ifdef HAVE_SENSORS_SENSORS_H + if (settings->showCPUTemperature) + LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs); + #endif +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + LinuxMachine* this = xCalloc(1, sizeof(LinuxMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + // Initialize page size + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); + this->pageSizeKB = this->pageSize / ONE_K; + + // Initialize clock ticks + if ((this->jiffies = sysconf(_SC_CLK_TCK)) == -1) + CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)"); + + // Read btime (the kernel boot time, as number of seconds since the epoch) + FILE* statfile = fopen(PROCSTATFILE, "r"); + if (statfile == NULL) + CRT_fatalError("Cannot open " PROCSTATFILE); + + this->boottime = -1; + + while (true) { + char buffer[PROC_LINE_LENGTH + 1]; + if (fgets(buffer, sizeof(buffer), statfile) == NULL) + break; + if (String_startsWith(buffer, "btime ") == false) + continue; + if (sscanf(buffer, "btime %lld\n", &this->boottime) == 1) + break; + CRT_fatalError("Failed to parse btime from " PROCSTATFILE); + } + fclose(statfile); + + if (this->boottime == -1) + CRT_fatalError("No btime in " PROCSTATFILE); + + // Initialize CPU count + LinuxMachine_updateCPUcount(this); + + return super; +} + +void Machine_delete(Machine* super) { + LinuxMachine* this = (LinuxMachine*) super; + Machine_done(super); + free(this->cpuData); + free(this); +} + +bool Machine_isCPUonline(const Machine* super, unsigned int id) { + const LinuxMachine* this = (const LinuxMachine*) super; + + assert(id < super->existingCPUs); + return this->cpuData[id + 1].online; +} diff --git a/linux/LinuxMachine.h b/linux/LinuxMachine.h new file mode 100644 index 00000000..7dba905f --- /dev/null +++ b/linux/LinuxMachine.h @@ -0,0 +1,110 @@ +#ifndef HEADER_LinuxMachine +#define HEADER_LinuxMachine +/* +htop - LinuxMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" + +#include +#include + +#include "Machine.h" +#include "UsersTable.h" +#include "linux/ZramStats.h" +#include "linux/ZswapStats.h" +#include "zfs/ZfsArcStats.h" + +#define HTOP_HUGEPAGE_BASE_SHIFT 16 +#define HTOP_HUGEPAGE_COUNT 24 + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int systemTime; + unsigned long long int systemAllTime; + unsigned long long int idleAllTime; + unsigned long long int idleTime; + unsigned long long int niceTime; + unsigned long long int ioWaitTime; + unsigned long long int irqTime; + unsigned long long int softIrqTime; + unsigned long long int stealTime; + unsigned long long int guestTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int systemPeriod; + unsigned long long int systemAllPeriod; + unsigned long long int idleAllPeriod; + unsigned long long int idlePeriod; + unsigned long long int nicePeriod; + unsigned long long int ioWaitPeriod; + unsigned long long int irqPeriod; + unsigned long long int softIrqPeriod; + unsigned long long int stealPeriod; + unsigned long long int guestPeriod; + + double frequency; + + #ifdef HAVE_SENSORS_SENSORS_H + double temperature; + #endif + + bool online; +} CPUData; + +typedef struct LinuxMachine_ { + Machine super; + + long jiffies; + long long boottime; + int pageSize; + int pageSizeKB; + + double period; + + CPUData* cpuData; + + memory_t totalHugePageMem; + memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT]; + + memory_t availableMem; + + ZfsArcStats zfs; + ZramStats zram; + ZswapStats zswap; +} LinuxMachine; + +#ifndef PROCDIR +#define PROCDIR "/proc" +#endif + +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + +#ifndef PROCSTATFILE +#define PROCSTATFILE PROCDIR "/stat" +#endif + +#ifndef PROCMEMINFOFILE +#define PROCMEMINFOFILE PROCDIR "/meminfo" +#endif + +#ifndef PROCARCSTATSFILE +#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" +#endif + +#ifndef PROCTTYDRIVERSFILE +#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" +#endif + +#ifndef PROC_LINE_LENGTH +#define PROC_LINE_LENGTH 4096 +#endif + +#endif diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index a3a4c821..b815c5b5 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -22,12 +22,9 @@ in the source distribution for its full text. #include "Scheduling.h" #include "XUtils.h" #include "linux/IOPriority.h" +#include "linux/LinuxMachine.h" -/* semi-global */ -int pageSize; -int pageSizeKB; - const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, @@ -197,6 +194,7 @@ bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) { static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) { const LinuxProcess* lp = (const LinuxProcess*) this; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; bool coloring = this->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; @@ -204,18 +202,18 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces switch (field) { case CMINFLT: Process_printCount(str, lp->cminflt, coloring); return; case CMAJFLT: Process_printCount(str, lp->cmajflt, coloring); return; - case M_DRS: Process_printBytes(str, lp->m_drs * pageSize, coloring); return; + case M_DRS: Process_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; case M_LRS: if (lp->m_lrs) { - Process_printBytes(str, lp->m_lrs * pageSize, coloring); + Process_printBytes(str, lp->m_lrs * lhost->pageSize, coloring); return; } attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, " N/A "); break; - case M_TRS: Process_printBytes(str, lp->m_trs * pageSize, coloring); return; - case M_SHARE: Process_printBytes(str, lp->m_share * pageSize, coloring); return; + case M_TRS: Process_printBytes(str, lp->m_trs * lhost->pageSize, coloring); return; + case M_SHARE: Process_printBytes(str, lp->m_share * lhost->pageSize, coloring); return; case M_PSS: Process_printKBytes(str, lp->m_pss, coloring); return; case M_SWAP: Process_printKBytes(str, lp->m_swap, coloring); return; case M_PSSWP: Process_printKBytes(str, lp->m_psswp, coloring); return; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 911fc3e1..e0a33e28 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -49,6 +49,7 @@ in the source distribution for its full text. #include "Settings.h" #include "XUtils.h" #include "linux/CGroupUtils.h" +#include "linux/LinuxMachine.h" #include "linux/LinuxProcess.h" #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep @@ -58,23 +59,11 @@ in the source distribution for its full text. #include #endif -#ifdef HAVE_SENSORS_SENSORS_H -#include "LibSensors.h" -#endif - -#ifndef O_PATH -#define O_PATH 010000000 // declare for ancient glibc versions -#endif - /* Not exposed yet. Defined at include/linux/sched.h */ #ifndef PF_KTHREAD #define PF_KTHREAD 0x00200000 #endif -static long long btime = -1; - -static long jiffy; - static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) { assert(String_eq(mode, "r")); /* only currently supported mode */ @@ -211,144 +200,22 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { #endif -static void LinuxProcessList_updateCPUcount(ProcessList* super) { - /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF - * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD - */ - - Machine* host = super->host; - LinuxProcessList* this = (LinuxProcessList*) super; - unsigned int existing = 0, active = 0; - - // Initialize the cpuData array before anything else. - if (!this->cpuData) { - this->cpuData = xCalloc(2, sizeof(CPUData)); - this->cpuData[0].online = true; /* average is always "online" */ - this->cpuData[1].online = true; - host->activeCPUs = 1; - host->existingCPUs = 1; - } - - DIR* dir = opendir("/sys/devices/system/cpu"); - if (!dir) - return; - - unsigned int currExisting = host->existingCPUs; - - const struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) - continue; - - if (!String_startsWith(entry->d_name, "cpu")) - continue; - - char* endp; - unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); - if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') - continue; - -#ifdef HAVE_OPENAT - int cpuDirFd = openat(dirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW); - if (cpuDirFd < 0) - continue; -#else - char cpuDirFd[4096]; - xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name); -#endif - - existing++; - - /* readdir() iterates with no specific order */ - unsigned int max = MAXIMUM(existing, id + 1); - if (max > currExisting) { - this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData)); - this->cpuData[0].online = true; /* average is always "online" */ - currExisting = max; - } - - char buffer[8]; - ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer)); - /* If the file "online" does not exist or on failure count as active */ - if (res < 1 || buffer[0] != '0') { - active++; - this->cpuData[id + 1].online = true; - } else { - this->cpuData[id + 1].online = false; - } - - Compat_openatArgClose(cpuDirFd); - } - - closedir(dir); - - // return if no CPU is found - if (existing < 1) - return; - -#ifdef HAVE_SENSORS_SENSORS_H - /* When started with offline CPUs, libsensors does not monitor those, - * even when they become online. */ - if (host->existingCPUs != 0 && (active > host->activeCPUs || currExisting > host->existingCPUs)) - LibSensors_reload(); -#endif - - host->activeCPUs = active; - assert(existing == currExisting); - host->existingCPUs = currExisting; -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); - ProcessList* pl = &(this->super); + ProcessList* super = &this->super; - ProcessList_init(pl, Class(LinuxProcess), host, pidMatchList); + ProcessList_init(super, Class(LinuxProcess), host, pidMatchList); LinuxProcessList_initTtyDrivers(this); - // Initialize page size - pageSize = sysconf(_SC_PAGESIZE); - if (pageSize == -1) - CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); - pageSizeKB = pageSize / ONE_K; - - // Initialize clock ticks - jiffy = sysconf(_SC_CLK_TCK); - if (jiffy == -1) - CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)"); - // Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+) this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0); - // Read btime (the kernel boot time, as number of seconds since the epoch) - FILE* statfile = fopen(PROCSTATFILE, "r"); - if (statfile == NULL) - CRT_fatalError("Cannot open " PROCSTATFILE); - while (true) { - char buffer[PROC_LINE_LENGTH + 1]; - if (fgets(buffer, sizeof(buffer), statfile) == NULL) - break; - if (String_startsWith(buffer, "btime ") == false) - continue; - if (sscanf(buffer, "btime %lld\n", &btime) == 1) - break; - CRT_fatalError("Failed to parse btime from " PROCSTATFILE); - } - - fclose(statfile); - - if (btime == -1) - CRT_fatalError("No btime in " PROCSTATFILE); - - // Initialize CPU count - LinuxProcessList_updateCPUcount(pl); - - return pl; + return super; } void ProcessList_delete(ProcessList* pl) { LinuxProcessList* this = (LinuxProcessList*) pl; ProcessList_done(pl); - free(this->cpuData); if (this->ttyDrivers) { for (int i = 0; this->ttyDrivers[i].path; i++) { free(this->ttyDrivers[i].path); @@ -364,8 +231,8 @@ void ProcessList_delete(ProcessList* pl) { free(this); } -static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) { - return t * 100 / jiffy; +static inline unsigned long long LinuxProcessList_adjustTime(const LinuxMachine* lhost, unsigned long long t) { + return t * 100 / lhost->jiffies; } /* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */ @@ -384,7 +251,7 @@ static inline ProcessState LinuxProcessList_getProcessState(char state) { } } -static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, char* command, size_t commLen) { +static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, const LinuxMachine* lhost, bool scanMainThread, char* command, size_t commLen) { Process* process = &lp->super; char buf[MAX_READ + 1]; @@ -457,19 +324,19 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, location += 1; /* (14) utime - %lu */ - lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->utime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (15) stime - %lu */ - lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->stime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (16) cutime - %ld */ - lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->cutime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (17) cstime - %ld */ - lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->cstime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (18) priority - %ld */ @@ -489,7 +356,7 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, /* (22) starttime - %llu */ if (process->starttime_ctime == 0) { - process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100; + process->starttime_ctime = lhost->boottime + LinuxProcessList_adjustTime(lhost, strtoll(location, &location, 10)) / 100; } else { location = strchr(location, ' '); } @@ -613,8 +480,9 @@ static bool LinuxProcessList_updateUser(const Machine* host, Process* process, o return true; } -static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { +static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread) { Process* process = &lp->super; + const Machine* host = process->host; char path[20] = "io"; char buffer[1024]; if (scanMainThread) { @@ -631,13 +499,13 @@ static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, b lp->io_read_bytes = ULLONG_MAX; lp->io_write_bytes = ULLONG_MAX; lp->io_cancelled_write_bytes = ULLONG_MAX; - lp->io_last_scan_time_ms = realtimeMs; + lp->io_last_scan_time_ms = host->realtimeMs; return; } unsigned long long last_read = lp->io_read_bytes; unsigned long long last_write = lp->io_write_bytes; - unsigned long long time_delta = realtimeMs > lp->io_last_scan_time_ms ? realtimeMs - lp->io_last_scan_time_ms : 0; + unsigned long long time_delta = host->realtimeMs > lp->io_last_scan_time_ms ? host->realtimeMs - lp->io_last_scan_time_ms : 0; char* buf = buffer; const char* line; @@ -673,7 +541,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, b } } - lp->io_last_scan_time_ms = realtimeMs; + lp->io_last_scan_time_ms = host->realtimeMs; } typedef struct LibraryData_ { @@ -696,7 +564,7 @@ static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* *d += v->size; } -static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, bool calcSize, bool checkDeletedLib) { +static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host, bool calcSize, bool checkDeletedLib) { Process* proc = (Process*)process; proc->usesDeletedLib = false; @@ -801,11 +669,11 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd Hashtable_delete(ht); - process->m_lrs = total_size / pageSize; + process->m_lrs = total_size / host->pageSize; } } -static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd) { +static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host) { FILE* statmfile = fopenat(procFd, "statm", "r"); if (!statmfile) return false; @@ -823,8 +691,8 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p fclose(statmfile); if (r == 7) { - process->super.m_virt *= pageSizeKB; - process->super.m_resident *= pageSizeKB; + process->super.m_virt *= host->pageSizeKB; + process->super.m_resident *= host->pageSizeKB; } return r == 7; @@ -1422,14 +1290,14 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo return out; } -static bool isOlderThan(const Machine* host, const Process* proc, unsigned int seconds) { - assert(host->realtimeMs > 0); +static bool isOlderThan(const Process* proc, unsigned int seconds) { + assert(proc->host->realtimeMs > 0); /* Starttime might not yet be parsed */ if (proc->starttime_ctime <= 0) return false; - uint64_t realtime = host->realtimeMs / 1000; + uint64_t realtime = proc->host->realtimeMs / 1000; if (realtime < (uint64_t)proc->starttime_ctime) return false; @@ -1437,9 +1305,9 @@ static bool isOlderThan(const Machine* host, const Process* proc, unsigned int s return realtime - proc->starttime_ctime > seconds; } -static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) { +static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const LinuxMachine* lhost, const char* dirname, const Process* parent) { ProcessList* pl = (ProcessList*) this; - const Machine* host = pl->host; + const Machine* host = &lhost->super; const Settings* settings = host->settings; const ScreenSettings* ss = settings->ss; const struct dirent* entry; @@ -1459,7 +1327,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ return false; } - const unsigned int activeCPUs = host->activeCPUs; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; const bool hideRunningInContainer = settings->hideRunningInContainer; @@ -1512,7 +1379,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ proc->tgid = parent ? parent->pid : pid; proc->isUserlandThread = proc->pid != proc->tgid; - LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period); + LinuxProcessList_recurseProcTree(this, procFd, lhost, "task", proc); /* * These conditions will not trigger on first occurrence, cause we need to @@ -1545,16 +1412,16 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent; if (ss->flags & PROCESS_FLAG_IO) - LinuxProcessList_readIoFile(lp, procFd, scanMainThread, host->realtimeMs); + LinuxProcessList_readIoFile(lp, procFd, scanMainThread); - if (!LinuxProcessList_readStatmFile(lp, procFd)) + if (!LinuxProcessList_readStatmFile(lp, procFd, lhost)) goto errorReadingProcess; { bool prev = proc->usesDeletedLib; if (!proc->isKernelThread && !proc->isUserlandThread && - ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(host, proc, 10)))) { + ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(proc, 10)))) { // Check if we really should recalculate the M_LRS value for this process uint64_t passedTimeInMs = host->realtimeMs - lp->last_mlrs_calctime; @@ -1563,7 +1430,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (passedTimeInMs > recheck) { lp->last_mlrs_calctime = host->realtimeMs; - LinuxProcessList_readMaps(lp, procFd, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); + LinuxProcessList_readMaps(lp, procFd, lhost, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); } } else { /* Copy from process structure in threads and reset if setting got disabled */ @@ -1593,7 +1460,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ char statCommand[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); unsigned long int tty_nr = proc->tty_nr; - if (!LinuxProcessList_readStatFile(lp, procFd, scanMainThread, statCommand, sizeof(statCommand))) + if (!LinuxProcessList_readStatFile(lp, procFd, lhost, scanMainThread, statCommand, sizeof(statCommand))) goto errorReadingProcess; if (lp->flags & PF_KTHREAD) { @@ -1610,8 +1477,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } /* period might be 0 after system sleep */ - float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0); - proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F); + float percent_cpu = (lhost->period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / lhost->period * 100.0); + proc->percent_cpu = CLAMP(percent_cpu, 0.0F, host->activeCPUs * 100.0F); proc->percent_mem = proc->m_resident / (double)(host->totalMem) * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); @@ -1706,7 +1573,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); pl->totalTasks++; - /* runningTasks is set in LinuxProcessList_scanCPUTime() from /proc/stat */ + /* runningTasks is set in Machine_scanCPUTime() from /proc/stat */ continue; // Exception handler. @@ -1734,546 +1601,11 @@ errorReadingProcess: return true; } -static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { - Machine* host = this->host; - LinuxProcessList *lpl = (LinuxProcessList *)this; - memory_t availableMem = 0; - memory_t freeMem = 0; - memory_t totalMem = 0; - memory_t buffersMem = 0; - memory_t cachedMem = 0; - memory_t sharedMem = 0; - memory_t swapTotalMem = 0; - memory_t swapCacheMem = 0; - memory_t swapFreeMem = 0; - memory_t sreclaimableMem = 0; - memory_t zswapCompMem = 0; - memory_t zswapOrigMem = 0; - - FILE* file = fopen(PROCMEMINFOFILE, "r"); - if (!file) - CRT_fatalError("Cannot open " PROCMEMINFOFILE); - - char buffer[128]; - while (fgets(buffer, sizeof(buffer), file)) { - - #define tryRead(label, variable) \ - if (String_startsWith(buffer, label)) { \ - memory_t parsed_; \ - if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \ - (variable) = parsed_; \ - } \ - break; \ - } else (void) 0 /* Require a ";" after the macro use. */ - - switch (buffer[0]) { - case 'M': - tryRead("MemAvailable:", availableMem); - tryRead("MemFree:", freeMem); - tryRead("MemTotal:", totalMem); - break; - case 'B': - tryRead("Buffers:", buffersMem); - break; - case 'C': - tryRead("Cached:", cachedMem); - break; - case 'S': - switch (buffer[1]) { - case 'h': - tryRead("Shmem:", sharedMem); - break; - case 'w': - tryRead("SwapTotal:", swapTotalMem); - tryRead("SwapCached:", swapCacheMem); - tryRead("SwapFree:", swapFreeMem); - break; - case 'R': - tryRead("SReclaimable:", sreclaimableMem); - break; - } - break; - case 'Z': - tryRead("Zswap:", zswapCompMem); - tryRead("Zswapped:", zswapOrigMem); - break; - } - - #undef tryRead - } - - fclose(file); - - /* - * Compute memory partition like procps(free) - * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c - * - * Adjustments: - * - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/), - * do not show twice by subtracting from Cached and do not subtract twice from used. - */ - host->totalMem = totalMem; - host->cachedMem = cachedMem + sreclaimableMem - sharedMem; - host->sharedMem = sharedMem; - const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; - host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; - host->buffersMem = buffersMem; - host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; - host->totalSwap = swapTotalMem; - host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; - host->cachedSwap = swapCacheMem; - lpl->zswap.usedZswapComp = zswapCompMem; - lpl->zswap.usedZswapOrig = zswapOrigMem; -} - -static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { - this->totalHugePageMem = 0; - for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { - this->usedHugePageMem[i] = MEMORY_MAX; - } - - DIR* dir = opendir("/sys/kernel/mm/hugepages"); - if (!dir) - return; - - const struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - const char* name = entry->d_name; - - /* Ignore all non-directories */ - if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) - continue; - - if (!String_startsWith(name, "hugepages-")) - continue; - - char* endptr; - unsigned long int hugePageSize = strtoul(name + strlen("hugepages-"), &endptr, 10); - if (!endptr || *endptr != 'k') - continue; - - char content[64]; - char hugePagePath[128]; - ssize_t r; - - xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/nr_hugepages", name); - r = xReadfile(hugePagePath, content, sizeof(content)); - if (r <= 0) - continue; - - memory_t total = strtoull(content, NULL, 10); - if (total == 0) - continue; - - xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/free_hugepages", name); - r = xReadfile(hugePagePath, content, sizeof(content)); - if (r <= 0) - continue; - - memory_t free = strtoull(content, NULL, 10); - - int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10); - assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT); - - this->totalHugePageMem += total * hugePageSize; - this->usedHugePageMem[shift] = (total - free) * hugePageSize; - } - - closedir(dir); -} - -static inline void LinuxProcessList_scanZswapInfo(LinuxProcessList *this) { - const Machine* host = this->super.host; - long max_pool_percent = 0; - int r; - char buf[256]; - - r = xReadfile("/sys/module/zswap/parameters/max_pool_percent", buf, 256); - if (r <= 0) { - return; - } - max_pool_percent = strtol(buf, NULL, 10); - if (max_pool_percent < 0 || max_pool_percent > 100) { - return; - } - - this->zswap.totalZswapPool = host->totalMem * max_pool_percent / 100; - /* the rest of the metrics are set in LinuxProcessList_scanMemoryInfo() */ -} - -static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { - memory_t totalZram = 0; - memory_t usedZramComp = 0; - memory_t usedZramOrig = 0; - - char mm_stat[34]; - char disksize[34]; - - unsigned int i = 0; - for (;;) { - xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i); - xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i); - i++; - FILE* disksize_file = fopen(disksize, "r"); - FILE* mm_stat_file = fopen(mm_stat, "r"); - if (disksize_file == NULL || mm_stat_file == NULL) { - if (disksize_file) { - fclose(disksize_file); - } - if (mm_stat_file) { - fclose(mm_stat_file); - } - break; - } - memory_t size = 0; - memory_t orig_data_size = 0; - memory_t compr_data_size = 0; - - if (!fscanf(disksize_file, "%llu\n", &size) || - !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) { - fclose(disksize_file); - fclose(mm_stat_file); - break; - } - - totalZram += size; - usedZramComp += compr_data_size; - usedZramOrig += orig_data_size; - - fclose(disksize_file); - fclose(mm_stat_file); - } - - this->zram.totalZram = totalZram / 1024; - this->zram.usedZramComp = usedZramComp / 1024; - this->zram.usedZramOrig = usedZramOrig / 1024; -} - -static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { - memory_t dbufSize = 0; - memory_t dnodeSize = 0; - memory_t bonusSize = 0; - - FILE* file = fopen(PROCARCSTATSFILE, "r"); - if (file == NULL) { - lpl->zfs.enabled = 0; - return; - } - char buffer[128]; - while (fgets(buffer, 128, file)) { - #define tryRead(label, variable) \ - if (String_startsWith(buffer, label)) { \ - sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ - break; \ - } else (void) 0 /* Require a ";" after the macro use. */ - #define tryReadFlag(label, variable, flag) \ - if (String_startsWith(buffer, label)) { \ - (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ - break; \ - } else (void) 0 /* Require a ";" after the macro use. */ - - switch (buffer[0]) { - case 'c': - tryRead("c_min", &lpl->zfs.min); - tryRead("c_max", &lpl->zfs.max); - tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed); - break; - case 'u': - tryRead("uncompressed_size", &lpl->zfs.uncompressed); - break; - case 's': - tryRead("size", &lpl->zfs.size); - break; - case 'h': - tryRead("hdr_size", &lpl->zfs.header); - break; - case 'd': - tryRead("dbuf_size", &dbufSize); - tryRead("dnode_size", &dnodeSize); - break; - case 'b': - tryRead("bonus_size", &bonusSize); - break; - case 'a': - tryRead("anon_size", &lpl->zfs.anon); - break; - case 'm': - tryRead("mfu_size", &lpl->zfs.MFU); - tryRead("mru_size", &lpl->zfs.MRU); - break; - } - #undef tryRead - #undef tryReadFlag - } - fclose(file); - - lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0); - lpl->zfs.size /= 1024; - lpl->zfs.min /= 1024; - lpl->zfs.max /= 1024; - lpl->zfs.MFU /= 1024; - lpl->zfs.MRU /= 1024; - lpl->zfs.anon /= 1024; - lpl->zfs.header /= 1024; - lpl->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024; - if ( lpl->zfs.isCompressed ) { - lpl->zfs.compressed /= 1024; - lpl->zfs.uncompressed /= 1024; - } -} - -static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { +void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; - - LinuxProcessList_updateCPUcount(super); - - FILE* file = fopen(PROCSTATFILE, "r"); - if (!file) - CRT_fatalError("Cannot open " PROCSTATFILE); - - Machine* host = super->host; - unsigned int existingCPUs = host->existingCPUs; - unsigned int lastAdjCpuId = 0; - - for (unsigned int i = 0; i <= existingCPUs; i++) { - char buffer[PROC_LINE_LENGTH + 1]; - unsigned long long int usertime, nicetime, systemtime, idletime; - unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0; - - const char* ok = fgets(buffer, sizeof(buffer), file); - if (!ok) - break; - - // cpu fields are sorted first - if (!String_startsWith(buffer, "cpu")) - break; - - // Depending on your kernel version, - // 5, 7, 8 or 9 of these fields will be set. - // The rest will remain at zero. - unsigned int adjCpuId; - if (i == 0) { - (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); - adjCpuId = 0; - } else { - unsigned int cpuid; - (void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); - adjCpuId = cpuid + 1; - } - - if (adjCpuId > host->existingCPUs) - break; - - for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) { - // Skipped an ID, but /proc/stat is ordered => got offline CPU - memset(&(this->cpuData[j]), '\0', sizeof(CPUData)); - } - lastAdjCpuId = adjCpuId; - - // Guest time is already accounted in usertime - usertime -= guest; - nicetime -= guestnice; - // Fields existing on kernels >= 2.6 - // (and RHEL's patched kernel 2.4...) - unsigned long long int idlealltime = idletime + ioWait; - unsigned long long int systemalltime = systemtime + irq + softIrq; - unsigned long long int virtalltime = guest + guestnice; - unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; - CPUData* cpuData = &(this->cpuData[adjCpuId]); - // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() - // used in /proc/stat rounds down numbers, it can lead to a case where the - // integer overflow. - cpuData->userPeriod = saturatingSub(usertime, cpuData->userTime); - cpuData->nicePeriod = saturatingSub(nicetime, cpuData->niceTime); - cpuData->systemPeriod = saturatingSub(systemtime, cpuData->systemTime); - cpuData->systemAllPeriod = saturatingSub(systemalltime, cpuData->systemAllTime); - cpuData->idleAllPeriod = saturatingSub(idlealltime, cpuData->idleAllTime); - cpuData->idlePeriod = saturatingSub(idletime, cpuData->idleTime); - cpuData->ioWaitPeriod = saturatingSub(ioWait, cpuData->ioWaitTime); - cpuData->irqPeriod = saturatingSub(irq, cpuData->irqTime); - cpuData->softIrqPeriod = saturatingSub(softIrq, cpuData->softIrqTime); - cpuData->stealPeriod = saturatingSub(steal, cpuData->stealTime); - cpuData->guestPeriod = saturatingSub(virtalltime, cpuData->guestTime); - cpuData->totalPeriod = saturatingSub(totaltime, cpuData->totalTime); - cpuData->userTime = usertime; - cpuData->niceTime = nicetime; - cpuData->systemTime = systemtime; - cpuData->systemAllTime = systemalltime; - cpuData->idleAllTime = idlealltime; - cpuData->idleTime = idletime; - cpuData->ioWaitTime = ioWait; - cpuData->irqTime = irq; - cpuData->softIrqTime = softIrq; - cpuData->stealTime = steal; - cpuData->guestTime = virtalltime; - cpuData->totalTime = totaltime; - } - - double period = (double)this->cpuData[0].totalPeriod / host->activeCPUs; - - char buffer[PROC_LINE_LENGTH + 1]; - while (fgets(buffer, sizeof(buffer), file)) { - if (String_startsWith(buffer, "procs_running")) { - super->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); - break; - } - } - - fclose(file); - - return period; -} - -static int scanCPUFrequencyFromSysCPUFreq(LinuxProcessList* this) { - const Machine* host = this->super.host; - unsigned int existingCPUs = host->existingCPUs; - int numCPUsWithFrequency = 0; - unsigned long totalFrequency = 0; - - /* - * On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow (> 1ms). This delay - * accumulates for every core. For details see issue#471. - * If the read on CPU 0 takes longer than 500us bail out and fall back to reading the - * frequencies from /proc/cpuinfo. - * Once the condition has been met, bail out early for the next couple of scans. - */ - static int timeout = 0; - - if (timeout > 0) { - timeout--; - return -1; - } - - for (unsigned int i = 0; i < existingCPUs; ++i) { - if (!Machine_isCPUonline(host, i)) - continue; - - char pathBuffer[64]; - xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); - - struct timespec start; - if (i == 0) - clock_gettime(CLOCK_MONOTONIC, &start); - - FILE* file = fopen(pathBuffer, "r"); - if (!file) - return -errno; - - unsigned long frequency; - if (fscanf(file, "%lu", &frequency) == 1) { - /* convert kHz to MHz */ - frequency = frequency / 1000; - this->cpuData[i + 1].frequency = frequency; - numCPUsWithFrequency++; - totalFrequency += frequency; - } - - fclose(file); - - if (i == 0) { - struct timespec end; - clock_gettime(CLOCK_MONOTONIC, &end); - const time_t timeTakenUs = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000; - if (timeTakenUs > 500) { - timeout = 30; - return -1; - } - } - - } - - if (numCPUsWithFrequency > 0) - this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency; - - return 0; -} - -static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) { - FILE* file = fopen(PROCCPUINFOFILE, "r"); - if (file == NULL) - return; - - unsigned int existingCPUs = this->super.host->existingCPUs; - int numCPUsWithFrequency = 0; - double totalFrequency = 0; - int cpuid = -1; - - while (!feof(file)) { - double frequency; - char buffer[PROC_LINE_LENGTH]; - - if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) - break; - - if (sscanf(buffer, "processor : %d", &cpuid) == 1) { - continue; - } else if ( - (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || - (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) - ) { - if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) { - continue; - } - - CPUData* cpuData = &(this->cpuData[cpuid + 1]); - /* do not override sysfs data */ - if (isnan(cpuData->frequency)) { - cpuData->frequency = frequency; - } - numCPUsWithFrequency++; - totalFrequency += frequency; - } else if (buffer[0] == '\n') { - cpuid = -1; - } - } - fclose(file); - - if (numCPUsWithFrequency > 0) { - this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency; - } -} - -static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { - unsigned int existingCPUs = this->super.host->existingCPUs; - - for (unsigned int i = 0; i <= existingCPUs; i++) { - this->cpuData[i].frequency = NAN; - } - - if (scanCPUFrequencyFromSysCPUFreq(this) == 0) { - return; - } - - scanCPUFrequencyFromCPUinfo(this); -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - LinuxProcessList* this = (LinuxProcessList*) super; - const Machine* host = super->host; const Settings* settings = host->settings; - - LinuxProcessList_scanMemoryInfo(super); - LinuxProcessList_scanHugePages(this); - LinuxProcessList_scanZfsArcstats(this); - LinuxProcessList_scanZramInfo(this); - LinuxProcessList_scanZswapInfo(this); - - double period = LinuxProcessList_scanCPUTime(super); - - if (settings->showCPUFrequency) { - LinuxProcessList_scanCPUFrequency(this); - } - - #ifdef HAVE_SENSORS_SENSORS_H - if (settings->showCPUTemperature) - LibSensors_getCPUTemperatures(this->cpuData, host->existingCPUs, host->activeCPUs); - #endif - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } + const LinuxMachine* lhost = (const LinuxMachine*) host; if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) { // Refer to sched(7) 'autogroup feature' section @@ -2293,22 +1625,5 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { openat_arg_t rootFd = ""; #endif - LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period); -} - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - const LinuxProcessList* this = (const LinuxProcessList*) host->pl; - return this->cpuData[id + 1].online; + LinuxProcessList_recurseProcTree(this, rootFd, lhost, PROCDIR, NULL); } diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 69bae5c0..824de482 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -15,48 +15,7 @@ in the source distribution for its full text. #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" -#include "linux/ZramStats.h" -#include "linux/ZswapStats.h" -#include "zfs/ZfsArcStats.h" -#define HTOP_HUGEPAGE_BASE_SHIFT 16 -#define HTOP_HUGEPAGE_COUNT 24 - -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int systemTime; - unsigned long long int systemAllTime; - unsigned long long int idleAllTime; - unsigned long long int idleTime; - unsigned long long int niceTime; - unsigned long long int ioWaitTime; - unsigned long long int irqTime; - unsigned long long int softIrqTime; - unsigned long long int stealTime; - unsigned long long int guestTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int systemPeriod; - unsigned long long int systemAllPeriod; - unsigned long long int idleAllPeriod; - unsigned long long int idlePeriod; - unsigned long long int nicePeriod; - unsigned long long int ioWaitPeriod; - unsigned long long int irqPeriod; - unsigned long long int softIrqPeriod; - unsigned long long int stealPeriod; - unsigned long long int guestPeriod; - - double frequency; - - #ifdef HAVE_SENSORS_SENSORS_H - double temperature; - #endif - - bool online; -} CPUData; typedef struct TtyDriver_ { char* path; @@ -68,8 +27,6 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - CPUData* cpuData; - TtyDriver* ttyDrivers; bool haveSmapsRollup; bool haveAutogroup; @@ -78,55 +35,6 @@ typedef struct LinuxProcessList_ { struct nl_sock* netlink_socket; int netlink_family; #endif - - memory_t totalHugePageMem; - memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT]; - - memory_t availableMem; - - ZfsArcStats zfs; - ZramStats zram; - ZswapStats zswap; } LinuxProcessList; -#ifndef PROCDIR -#define PROCDIR "/proc" -#endif - -#ifndef PROCCPUINFOFILE -#define PROCCPUINFOFILE PROCDIR "/cpuinfo" -#endif - -#ifndef PROCSTATFILE -#define PROCSTATFILE PROCDIR "/stat" -#endif - -#ifndef PROCMEMINFOFILE -#define PROCMEMINFOFILE PROCDIR "/meminfo" -#endif - -#ifndef PROCARCSTATSFILE -#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" -#endif - -#ifndef PROCTTYDRIVERSFILE -#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" -#endif - -#ifndef PROC_LINE_LENGTH -#define PROC_LINE_LENGTH 4096 -#endif - -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* pl); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/linux/Platform.c b/linux/Platform.c index 68cefce9..693044af 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -36,6 +36,7 @@ in the source distribution for its full text. #include "HostnameMeter.h" #include "HugePageMeter.h" #include "LoadAverageMeter.h" +#include "Machine.h" #include "Macros.h" #include "MainPanel.h" #include "Meter.h" @@ -45,7 +46,6 @@ in the source distribution for its full text. #include "Object.h" #include "Panel.h" #include "PressureStallMeter.h" -#include "ProcessList.h" #include "ProvideCurses.h" #include "linux/SELinuxMeter.h" #include "Settings.h" @@ -56,8 +56,8 @@ in the source distribution for its full text. #include "XUtils.h" #include "linux/IOPriority.h" #include "linux/IOPriorityPanel.h" +#include "linux/LinuxMachine.h" #include "linux/LinuxProcess.h" -#include "linux/LinuxProcessList.h" #include "linux/SystemdMeter.h" #include "linux/ZramMeter.h" #include "linux/ZramStats.h" @@ -304,9 +304,9 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const Machine* host = this->host; - const LinuxProcessList* pl = (const LinuxProcessList*) host->pl; - const CPUData* cpuData = &(pl->cpuData[cpu]); + const LinuxMachine* lhost = (const LinuxMachine *) this->host; + const Settings* settings = this->host->settings; + const CPUData* cpuData = &(lhost->cpuData[cpu]); double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); double percent; double* v = this->values; @@ -318,7 +318,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; - if (host->settings->detailedCPUTime) { + if (settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0; @@ -327,7 +327,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0; this->curItems = 8; percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; - if (host->settings->accountGuestInCPUMeter) { + if (settings->accountGuestInCPUMeter) { percent += v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } } else { @@ -354,7 +354,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - const LinuxProcessList* lpl = (const LinuxProcessList*) host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) host; this->total = host->totalMem; this->values[MEMORY_METER_USED] = host->usedMem; @@ -364,32 +364,32 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; this->values[MEMORY_METER_AVAILABLE] = host->availableMem; - if (lpl->zfs.enabled != 0 && !Running_containerized) { + if (lhost->zfs.enabled != 0 && !Running_containerized) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (lpl->zfs.size > lpl->zfs.min) - shrinkableSize = lpl->zfs.size - lpl->zfs.min; + if (lhost->zfs.size > lhost->zfs.min) + shrinkableSize = lhost->zfs.size - lhost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } - if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { - this->values[MEMORY_METER_USED] -= lpl->zswap.usedZswapComp; - this->values[MEMORY_METER_COMPRESSED] += lpl->zswap.usedZswapComp; + if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) { + this->values[MEMORY_METER_USED] -= lhost->zswap.usedZswapComp; + this->values[MEMORY_METER_COMPRESSED] += lhost->zswap.usedZswapComp; } } void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; - const LinuxProcessList* lpl = (const LinuxProcessList*) host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) host; this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; this->values[SWAP_METER_CACHE] = host->cachedSwap; this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */ - if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { + if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) { /* * FIXME: Zswapped pages can be both SwapUsed and SwapCached, and we do not know which. * @@ -400,33 +400,34 @@ void Platform_setSwapValues(Meter* this) { * For now, subtract Zswapped from SwapUsed and only if Zswapped > SwapUsed, subtract the * overflow from SwapCached. */ - this->values[SWAP_METER_USED] -= lpl->zswap.usedZswapOrig; + this->values[SWAP_METER_USED] -= lhost->zswap.usedZswapOrig; if (this->values[SWAP_METER_USED] < 0) { /* subtract the overflow from SwapCached */ this->values[SWAP_METER_CACHE] += this->values[SWAP_METER_USED]; this->values[SWAP_METER_USED] = 0; } - this->values[SWAP_METER_FRONTSWAP] += lpl->zswap.usedZswapOrig; + this->values[SWAP_METER_FRONTSWAP] += lhost->zswap.usedZswapOrig; } } void Platform_setZramValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; - this->total = lpl->zram.totalZram; - this->values[ZRAM_METER_COMPRESSED] = lpl->zram.usedZramComp; - this->values[ZRAM_METER_UNCOMPRESSED] = lpl->zram.usedZramOrig; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; + + this->total = lhost->zram.totalZram; + this->values[ZRAM_METER_COMPRESSED] = lhost->zram.usedZramComp; + this->values[ZRAM_METER_UNCOMPRESSED] = lhost->zram.usedZramOrig; } void Platform_setZfsArcValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; - ZfsArcMeter_readStats(this, &(lpl->zfs)); + ZfsArcMeter_readStats(this, &(lhost->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(lpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &(lhost->zfs)); } char* Platform_getProcessEnv(pid_t pid) { diff --git a/netbsd/NetBSDMachine.c b/netbsd/NetBSDMachine.c new file mode 100644 index 00000000..1e2a0a13 --- /dev/null +++ b/netbsd/NetBSDMachine.c @@ -0,0 +1,282 @@ +/* +htop - NetBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "netbsd/NetBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Machine.h" +#include "Macros.h" +#include "Object.h" +#include "Settings.h" +#include "XUtils.h" + + +static const struct { + const char* name; + long int scale; +} freqSysctls[] = { + { "machdep.est.frequency.current", 1 }, + { "machdep.powernow.frequency.current", 1 }, + { "machdep.intrepid.frequency.current", 1 }, + { "machdep.loongson.frequency.current", 1 }, + { "machdep.cpu.frequency.current", 1 }, + { "machdep.frequency.current", 1 }, + { "machdep.tsc_freq", 1000000 }, +}; + +static void NetBSDMachine_updateCPUcount(NetBSDMachine* this) { + Machine* super = &this->super; + + // Definitions for sysctl(3), cf. https://nxr.netbsd.org/xref/src/sys/sys/sysctl.h#813 + const int mib_ncpu_existing[] = { CTL_HW, HW_NCPU }; // Number of existing CPUs + const int mib_ncpu_online[] = { CTL_HW, HW_NCPUONLINE }; // Number of online/active CPUs + + int r; + unsigned int value; + size_t size; + + bool change = false; + + // Query the number of active/online CPUs. + size = sizeof(value); + r = sysctl(mib_ncpu_online, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = 1; + } + + if (value != super->activeCPUs) { + super->activeCPUs = value; + change = true; + } + + // Query the total number of CPUs. + size = sizeof(value); + r = sysctl(mib_ncpu_existing, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = super->activeCPUs; + } + + if (value != super->existingCPUs) { + opl->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData)); + super->existingCPUs = value; + change = true; + } + + // Reset CPU stats when number of online/existing CPU cores changed + if (change) { + CPUData* dAvg = &this->cpuData[0]; + memset(dAvg, '\0', sizeof(CPUData)); + dAvg->totalTime = 1; + dAvg->totalPeriod = 1; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* d = &this->cpuData[i + 1]; + memset(d, '\0', sizeof(CPUData)); + d->totalTime = 1; + d->totalPeriod = 1; + } + } +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + const int fmib[] = { CTL_KERN, KERN_FSCALE }; + size_t size; + char errbuf[_POSIX2_LINE_MAX]; + + NetBSDMachine* this = xCalloc(1, sizeof(NetBSDMachine)); + Machine* super = &this->super; + Machine_init(super, usersTable, userId); + + NetBSDMachine_updateCPUcount(this); + + size = sizeof(this->fscale); + if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0) { + CRT_fatalError("fscale sysctl call failed"); + } + + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("pagesize sysconf call failed"); + this->pageSizeKB = this->pageSize / ONE_K; + + this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return this; +} + +void Machine_delete(Machine* super) { + NetBSDMachine* this = (NetBSDProcessList*) super; + + Machine_done(super); + + if (this->kd) { + kvm_close(this->kd); + } + free(this->cpuData); + free(this); +} + +static void NetBSDMachine_scanMemoryInfo(NetBSDMachine* this) { + Machine* super = &this->super; + + static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; + struct uvmexp_sysctl uvmexp; + size_t size_uvmexp = sizeof(uvmexp); + + if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { + CRT_fatalError("uvmexp sysctl call failed"); + } + + super->totalMem = uvmexp.npages * this->pageSizeKB; + super->buffersMem = 0; + super->cachedMem = (uvmexp.filepages + uvmexp.execpages) * this->pageSizeKB; + super->usedMem = (uvmexp.active + uvmexp.wired) * this->pageSizeKB; + super->totalSwap = uvmexp.swpages * this->pageSizeKB; + super->usedSwap = uvmexp.swpginuse * this->pageSizeKB; +} + +static void getKernelCPUTimes(int cpuId, u_int64_t* times) { + const int mib[] = { CTL_KERN, KERN_CP_TIME, cpuId }; + size_t length = sizeof(*times) * CPUSTATES; + if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { + CRT_fatalError("sysctl kern.cp_time2 failed"); + } +} + +static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { + unsigned long long totalTime = 0; + for (int i = 0; i < CPUSTATES; i++) { + totalTime += times[i]; + } + + unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; + + cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); + cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); + cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); + cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); + cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); + cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); + cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); + + cpu->totalTime = totalTime; + cpu->userTime = times[CP_USER]; + cpu->niceTime = times[CP_NICE]; + cpu->sysTime = times[CP_SYS]; + cpu->sysAllTime = sysAllTime; + cpu->intrTime = times[CP_INTR]; + cpu->idleTime = times[CP_IDLE]; +} + +static void NetBSDMachine_scanCPUTime(NetBSDMachine* this) { + const Machine* super = &this->super; + + u_int64_t kernelTimes[CPUSTATES] = {0}; + u_int64_t avg[CPUSTATES] = {0}; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + getKernelCPUTimes(i, kernelTimes); + CPUData* cpu = &this->cpuData[i + 1]; + kernelCPUTimesToHtop(kernelTimes, cpu); + + avg[CP_USER] += cpu->userTime; + avg[CP_NICE] += cpu->niceTime; + avg[CP_SYS] += cpu->sysTime; + avg[CP_INTR] += cpu->intrTime; + avg[CP_IDLE] += cpu->idleTime; + } + + for (int i = 0; i < CPUSTATES; i++) { + avg[i] /= super->activeCPUs; + } + + kernelCPUTimesToHtop(avg, &this->cpuData[0]); +} + +static void NetBSDMachine_scanCPUFrequency(NetBSDMachine* this) { + const Machine* super = &this->super; + unsigned int cpus = super->existingCPUs; + bool match = false; + char name[64]; + long int freq = 0; + size_t freqSize; + + for (unsigned int i = 0; i < cpus; i++) { + this->cpuData[i + 1].frequency = NAN; + } + + /* newer hardware supports per-core frequency, for e.g. ARM big.LITTLE */ + for (unsigned int i = 0; i < cpus; i++) { + xSnprintf(name, sizeof(name), "machdep.cpufreq.cpu%u.current", i); + freqSize = sizeof(freq); + if (sysctlbyname(name, &freq, &freqSize, NULL, 0) != -1) { + this->cpuData[i + 1].frequency = freq; /* already in MHz */ + match = true; + } + } + + if (match) { + return; + } + + /* + * Iterate through legacy sysctl nodes for single-core frequency until + * we find a match... + */ + for (size_t i = 0; i < ARRAYSIZE(freqSysctls); i++) { + freqSize = sizeof(freq); + if (sysctlbyname(freqSysctls[i].name, &freq, &freqSize, NULL, 0) != -1) { + freq /= freqSysctls[i].scale; /* scale to MHz */ + match = true; + break; + } + } + + if (match) { + for (unsigned int i = 0; i < cpus; i++) { + this->cpuData[i + 1].frequency = freq; + } + } +} + +void Machine_scan(Machine* super) { + NetBSDMachine* this = (NetBSDMachine*) super; + + NetBSDProcessList_scanMemoryInfo(this); + NetBSDProcessList_scanCPUTime(this); + + if (super->settings->showCPUFrequency) { + NetBSDProcessList_scanCPUFrequency(npl); + } +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + // TODO: Support detecting online / offline CPUs. + return true; +} diff --git a/netbsd/NetBSDMachine.h b/netbsd/NetBSDMachine.h new file mode 100644 index 00000000..57070d67 --- /dev/null +++ b/netbsd/NetBSDMachine.h @@ -0,0 +1,54 @@ +#ifndef HEADER_NetBSDMachine +#define HEADER_NetBSDMachine +/* +htop - NetBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Machine.h" +#include "ProcessList.h" + + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; + + double frequency; +} CPUData; + +typedef struct NetBSDProcessList_ { + ProcessList super; + kvm_t* kd; + + long fscale; + int pageSize; + int pageSizeKB; + + CPUData* cpuData; +} NetBSDProcessList; + +#endif diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c index b7b6915a..9e5a9be4 100644 --- a/netbsd/NetBSDProcessList.c +++ b/netbsd/NetBSDProcessList.c @@ -20,7 +20,6 @@ in the source distribution for its full text. #include #include #include -#include #include #include #include @@ -32,137 +31,23 @@ in the source distribution for its full text. #include "ProcessList.h" #include "Settings.h" #include "XUtils.h" +#include "netbsd/NetBSDMachine.h" #include "netbsd/NetBSDProcess.h" -static long fscale; -static int pageSize; -static int pageSizeKB; - -static const struct { - const char* name; - long int scale; -} freqSysctls[] = { - { "machdep.est.frequency.current", 1 }, - { "machdep.powernow.frequency.current", 1 }, - { "machdep.intrepid.frequency.current", 1 }, - { "machdep.loongson.frequency.current", 1 }, - { "machdep.cpu.frequency.current", 1 }, - { "machdep.frequency.current", 1 }, - { "machdep.tsc_freq", 1000000 }, -}; - -static void NetBSDProcessList_updateCPUcount(ProcessList* super) { - NetBSDProcessList* opl = (NetBSDProcessList*) super; - Machine* host = super->host; - - // Definitions for sysctl(3), cf. https://nxr.netbsd.org/xref/src/sys/sys/sysctl.h#813 - const int mib_ncpu_existing[] = { CTL_HW, HW_NCPU }; // Number of existing CPUs - const int mib_ncpu_online[] = { CTL_HW, HW_NCPUONLINE }; // Number of online/active CPUs - - int r; - unsigned int value; - size_t size; - - bool change = false; - - // Query the number of active/online CPUs. - size = sizeof(value); - r = sysctl(mib_ncpu_online, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = 1; - } - - if (value != host->activeCPUs) { - host->activeCPUs = value; - change = true; - } - - // Query the total number of CPUs. - size = sizeof(value); - r = sysctl(mib_ncpu_existing, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = host->activeCPUs; - } - - if (value != host->existingCPUs) { - opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); - host->existingCPUs = value; - change = true; - } - - // Reset CPU stats when number of online/existing CPU cores changed - if (change) { - CPUData* dAvg = &opl->cpuData[0]; - memset(dAvg, '\0', sizeof(CPUData)); - dAvg->totalTime = 1; - dAvg->totalPeriod = 1; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - CPUData* d = &opl->cpuData[i + 1]; - memset(d, '\0', sizeof(CPUData)); - d->totalTime = 1; - d->totalPeriod = 1; - } - } -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - const int fmib[] = { CTL_KERN, KERN_FSCALE }; - size_t size; - char errbuf[_POSIX2_LINE_MAX]; - - NetBSDProcessList* npl = xCalloc(1, sizeof(NetBSDProcessList)); - ProcessList* pl = (ProcessList*) npl; - ProcessList_init(pl, Class(NetBSDProcess), host, pidMatchList); + NetBSDProcessList* this = xCalloc(1, sizeof(NetBSDProcessList)); + ProcessList* super = (ProcessList*) this; - NetBSDProcessList_updateCPUcount(pl); - - size = sizeof(fscale); - if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { - CRT_fatalError("fscale sysctl call failed"); - } + ProcessList_init(super, Class(NetBSDProcess), host, pidMatchList); - if ((pageSize = sysconf(_SC_PAGESIZE)) == -1) - CRT_fatalError("pagesize sysconf call failed"); - pageSizeKB = pageSize / ONE_K; - - npl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - if (npl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - return pl; + return super; } void ProcessList_delete(ProcessList* this) { NetBSDProcessList* npl = (NetBSDProcessList*) this; - - if (npl->kd) { - kvm_close(npl->kd); - } - - free(npl->cpuData); - ProcessList_done(this); - free(this); -} - -static void NetBSDProcessList_scanMemoryInfo(Machine* host) { - static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; - struct uvmexp_sysctl uvmexp; - size_t size_uvmexp = sizeof(uvmexp); - - if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { - CRT_fatalError("uvmexp sysctl call failed"); - } - - host->totalMem = uvmexp.npages * pageSizeKB; - host->buffersMem = 0; - host->cachedMem = (uvmexp.filepages + uvmexp.execpages) * pageSizeKB; - host->usedMem = (uvmexp.active + uvmexp.wired) * pageSizeKB; - host->totalSwap = uvmexp.swpages * pageSizeKB; - host->usedSwap = uvmexp.swpginuse * pageSizeKB; + free(npl); } static void NetBSDProcessList_updateExe(const struct kinfo_proc2* kproc, Process* proc) { @@ -255,21 +140,22 @@ static void NetBSDProcessList_updateProcessName(kvm_t* kd, const struct kinfo_pr /* * Borrowed with modifications from NetBSD's top(1). */ -static double getpcpu(const struct kinfo_proc2* kp) { - if (fscale == 0) +static double getpcpu(const NetBSDMachine* nhost, const struct kinfo_proc2* kp) { + if (nhost->fscale == 0) return 0.0; - return 100.0 * (double)kp->p_pctcpu / fscale; + return 100.0 * (double)kp->p_pctcpu / nhost->fscale; } static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { const Machine* host = this->super.host; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; - const struct kinfo_proc2* kprocs = kvm_getproc2(this->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count); + const struct kinfo_proc2* kprocs = kvm_getproc2(nhost->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count); for (int i = 0; i < count; i++) { const struct kinfo_proc2* kproc = &kprocs[i]; @@ -302,10 +188,10 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { } NetBSDProcessList_updateExe(kproc, proc); - NetBSDProcessList_updateProcessName(this->kd, kproc, proc); + NetBSDProcessList_updateProcessName(nhost->kd, kproc, proc); } else { if (settings->updateProcessNames) { - NetBSDProcessList_updateProcessName(this->kd, kproc, proc); + NetBSDProcessList_updateProcessName(nhost->kd, kproc, proc); } } @@ -321,8 +207,8 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { proc->m_virt = kproc->p_vm_vsize; proc->m_resident = kproc->p_vm_rssize; - proc->percent_mem = (proc->m_resident * pageSizeKB) / (double)(host->totalMem) * 100.0; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, host->activeCPUs * 100.0); + proc->percent_mem = (proc->m_resident * nhost->pageSizeKB) / (double)(host->totalMem) * 100.0; + proc->percent_cpu = CLAMP(getpcpu(nhost, kproc), 0.0, host->activeCPUs * 100.0); Process_updateCPUFieldWidths(proc->percent_cpu); proc->nlwp = kproc->p_nlwps; @@ -334,7 +220,7 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { proc->majflt = kproc->p_uru_majflt; int nlwps = 0; - const struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps); + const struct kinfo_lwp* klwps = kvm_getlwps(nhost->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps); /* TODO: According to the link below, SDYING should be a regarded state */ /* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */ @@ -379,140 +265,8 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { } } -static void getKernelCPUTimes(int cpuId, u_int64_t* times) { - const int mib[] = { CTL_KERN, KERN_CP_TIME, cpuId }; - size_t length = sizeof(*times) * CPUSTATES; - if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { - CRT_fatalError("sysctl kern.cp_time2 failed"); - } -} - -static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { - unsigned long long totalTime = 0; - for (int i = 0; i < CPUSTATES; i++) { - totalTime += times[i]; - } - - unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; - - cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); - cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); - cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); - cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); - cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); - cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); - cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); - - cpu->totalTime = totalTime; - cpu->userTime = times[CP_USER]; - cpu->niceTime = times[CP_NICE]; - cpu->sysTime = times[CP_SYS]; - cpu->sysAllTime = sysAllTime; - cpu->intrTime = times[CP_INTR]; - cpu->idleTime = times[CP_IDLE]; -} - -static void NetBSDProcessList_scanCPUTime(NetBSDProcessList* this) { - const Machine* host = this->super.host; - u_int64_t kernelTimes[CPUSTATES] = {0}; - u_int64_t avg[CPUSTATES] = {0}; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - getKernelCPUTimes(i, kernelTimes); - CPUData* cpu = &this->cpuData[i + 1]; - kernelCPUTimesToHtop(kernelTimes, cpu); - - avg[CP_USER] += cpu->userTime; - avg[CP_NICE] += cpu->niceTime; - avg[CP_SYS] += cpu->sysTime; - avg[CP_INTR] += cpu->intrTime; - avg[CP_IDLE] += cpu->idleTime; - } - - for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= host->activeCPUs; - } - - kernelCPUTimesToHtop(avg, &this->cpuData[0]); -} - -static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) { - const Machine* host = this->super.host; - unsigned int cpus = host->existingCPUs; - bool match = false; - char name[64]; - long int freq = 0; - size_t freqSize; - - for (unsigned int i = 0; i < cpus; i++) { - this->cpuData[i + 1].frequency = NAN; - } - - /* newer hardware supports per-core frequency, for e.g. ARM big.LITTLE */ - for (unsigned int i = 0; i < cpus; i++) { - xSnprintf(name, sizeof(name), "machdep.cpufreq.cpu%u.current", i); - freqSize = sizeof(freq); - if (sysctlbyname(name, &freq, &freqSize, NULL, 0) != -1) { - this->cpuData[i + 1].frequency = freq; /* already in MHz */ - match = true; - } - } - - if (match) { - return; - } - - /* - * Iterate through legacy sysctl nodes for single-core frequency until - * we find a match... - */ - for (size_t i = 0; i < ARRAYSIZE(freqSysctls); i++) { - freqSize = sizeof(freq); - if (sysctlbyname(freqSysctls[i].name, &freq, &freqSize, NULL, 0) != -1) { - freq /= freqSysctls[i].scale; /* scale to MHz */ - match = true; - break; - } - } - - if (match) { - for (unsigned int i = 0; i < cpus; i++) { - this->cpuData[i + 1].frequency = freq; - } - } -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { +void ProcessList_goThroughEntries(ProcessList* super) { NetBSDProcessList* npl = (NetBSDProcessList*) super; - NetBSDProcessList_scanMemoryInfo(super->host); - NetBSDProcessList_scanCPUTime(npl); - - if (super->settings->showCPUFrequency) { - NetBSDProcessList_scanCPUFrequency(npl); - } - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - NetBSDProcessList_scanProcs(npl); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: Support detecting online / offline CPUs. - return true; -} diff --git a/netbsd/NetBSDProcessList.h b/netbsd/NetBSDProcessList.h index 0fe18b03..362d84fc 100644 --- a/netbsd/NetBSDProcessList.h +++ b/netbsd/NetBSDProcessList.h @@ -10,55 +10,15 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include #include "Hashtable.h" #include "ProcessList.h" -#include "UsersTable.h" -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int niceTime; - unsigned long long int sysTime; - unsigned long long int sysAllTime; - unsigned long long int spinTime; - unsigned long long int intrTime; - unsigned long long int idleTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int nicePeriod; - unsigned long long int sysPeriod; - unsigned long long int sysAllPeriod; - unsigned long long int spinPeriod; - unsigned long long int intrPeriod; - unsigned long long int idlePeriod; - - double frequency; -} CPUData; - typedef struct NetBSDProcessList_ { ProcessList super; - kvm_t* kd; - - CPUData* cpuData; } NetBSDProcessList; - -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/netbsd/Platform.c b/netbsd/Platform.c index ea81a26a..1d6509ff 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -45,7 +45,6 @@ in the source distribution for its full text. #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "Meter.h" -#include "ProcessList.h" #include "Settings.h" #include "SignalsPanel.h" #include "SwapMeter.h" @@ -54,8 +53,8 @@ in the source distribution for its full text. #include "UptimeMeter.h" #include "XUtils.h" #include "generic/fdstat_sysctl.h" +#include "netbsd/NetBSDMachine.h" #include "netbsd/NetBSDProcess.h" -#include "netbsd/NetBSDProcessList.h" /* * The older proplib APIs will be deprecated in NetBSD 10, but we still @@ -238,8 +237,8 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, int cpu) { const Machine* host = this->host; - const NetBSDProcessList* npl = (const NetBSDProcessList*) host->pl; - const CPUData* cpuData = &npl->cpuData[cpu]; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; + const CPUData* cpuData = &nhost->cpuData[cpu]; double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; double totalPercent; double* v = this->values; diff --git a/openbsd/OpenBSDMachine.c b/openbsd/OpenBSDMachine.c new file mode 100644 index 00000000..e3e30990 --- /dev/null +++ b/openbsd/OpenBSDMachine.c @@ -0,0 +1,286 @@ +/* +htop - OpenBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "openbsd/OpenBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Macros.h" +#include "Object.h" +#include "Settings.h" +#include "XUtils.h" + + +static void OpenBSDMachine_updateCPUcount(OpenBSDMachine* this) { + Machine* super = &this->super; + const int nmib[] = { CTL_HW, HW_NCPU }; + const int mib[] = { CTL_HW, HW_NCPUONLINE }; + int r; + unsigned int value; + size_t size; + bool change = false; + + size = sizeof(value); + r = sysctl(mib, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = 1; + } + + if (value != super->activeCPUs) { + super->activeCPUs = value; + change = true; + } + + size = sizeof(value); + r = sysctl(nmib, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = super->activeCPUs; + } + + if (value != super->existingCPUs) { + this->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData)); + super->existingCPUs = value; + change = true; + } + + if (change) { + CPUData* dAvg = &this->cpuData[0]; + memset(dAvg, '\0', sizeof(CPUData)); + dAvg->totalTime = 1; + dAvg->totalPeriod = 1; + dAvg->online = true; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* d = &this->cpuData[i + 1]; + memset(d, '\0', sizeof(CPUData)); + d->totalTime = 1; + d->totalPeriod = 1; + + const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i }; + struct cpustats cpu_stats; + + size = sizeof(cpu_stats); + if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) { + CRT_fatalError("ncmib sysctl call failed"); + } + d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE); + } + } +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + const int fmib[] = { CTL_KERN, KERN_FSCALE }; + size_t size; + char errbuf[_POSIX2_LINE_MAX]; + + OpenBSDMachine* this = xCalloc(1, sizeof(OpenBSDMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + OpenBSDProcessList_updateCPUcount(this); + + size = sizeof(this->fscale); + if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0) { + CRT_fatalError("fscale sysctl call failed"); + } + + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("pagesize sysconf call failed"); + this->pageSizeKB = this->pageSize / ONE_K; + + this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + this->cpuSpeed = -1; + + return this; +} + +void Machine_delete(Machine* super) { + OpenBSDMachine* this = (OpenBSDMachine*) super; + + if (this->kd) { + kvm_close(this->kd); + } + free(this->cpuData); + Machine_done(super); + free(this); +} + +static void OpenBSDMachine_scanMemoryInfo(OpenBSDMachine* this) { + Machine* host = &this->super; + const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; + struct uvmexp uvmexp; + size_t size_uvmexp = sizeof(uvmexp); + + if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { + CRT_fatalError("uvmexp sysctl call failed"); + } + + super->totalMem = uvmexp.npages * this->pageSizeKB; + super->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * this->pageSizeKB; + + // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) + const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; + struct bcachestats bcstats; + size_t size_bcstats = sizeof(bcstats); + + if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { + CRT_fatalError("cannot get vfs.bcachestat"); + } + + super->cachedMem = bcstats.numbufpages * this->pageSizeKB; + + /* + * Copyright (c) 1994 Thorsten Lockert + * All rights reserved. + * + * Taken almost directly from OpenBSD's top(1) + * + * Originally released under a BSD-3 license + * Modified through htop developers applying GPL-2 + */ + int nswap = swapctl(SWAP_NSWAP, 0, 0); + if (nswap > 0) { + struct swapent swdev[nswap]; + int rnswap = swapctl(SWAP_STATS, swdev, nswap); + + /* Total things up */ + unsigned long long int total = 0, used = 0; + for (int i = 0; i < rnswap; i++) { + if (swdev[i].se_flags & SWF_ENABLE) { + used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); + total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); + } + } + + super->totalSwap = total; + super->usedSwap = used; + } else { + super->totalSwap = super->usedSwap = 0; + } +} + +static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) { + const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; + size_t length = sizeof(*times) * CPUSTATES; + if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { + CRT_fatalError("sysctl kern.cp_time2 failed"); + } +} + +static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { + unsigned long long totalTime = 0; + for (int i = 0; i < CPUSTATES; i++) { + totalTime += times[i]; + } + + unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; + + // XXX Not sure if CP_SPIN should be added to sysAllTime. + // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c + #ifdef CP_SPIN + sysAllTime += times[CP_SPIN]; + #endif + + cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); + cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); + cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); + cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); + cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); + #ifdef CP_SPIN + cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime); + #endif + cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); + cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); + + cpu->totalTime = totalTime; + cpu->userTime = times[CP_USER]; + cpu->niceTime = times[CP_NICE]; + cpu->sysTime = times[CP_SYS]; + cpu->sysAllTime = sysAllTime; + #ifdef CP_SPIN + cpu->spinTime = times[CP_SPIN]; + #endif + cpu->intrTime = times[CP_INTR]; + cpu->idleTime = times[CP_IDLE]; +} + +static void OpenBSDMachine_scanCPUTime(OpenBSDMachine* this) { + Machine* host = &this->super; + u_int64_t kernelTimes[CPUSTATES] = {0}; + u_int64_t avg[CPUSTATES] = {0}; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* cpu = &this->cpuData[i + 1]; + + if (!cpu->online) { + continue; + } + + getKernelCPUTimes(i, kernelTimes); + kernelCPUTimesToHtop(kernelTimes, cpu); + + avg[CP_USER] += cpu->userTime; + avg[CP_NICE] += cpu->niceTime; + avg[CP_SYS] += cpu->sysTime; + #ifdef CP_SPIN + avg[CP_SPIN] += cpu->spinTime; + #endif + avg[CP_INTR] += cpu->intrTime; + avg[CP_IDLE] += cpu->idleTime; + } + + for (int i = 0; i < CPUSTATES; i++) { + avg[i] /= super->activeCPUs; + } + + kernelCPUTimesToHtop(avg, &this->cpuData[0]); + + { + const int mib[] = { CTL_HW, HW_CPUSPEED }; + int cpuSpeed; + size_t size = sizeof(cpuSpeed); + if (sysctl(mib, 2, &cpuSpeed, &size, NULL, 0) == -1) { + this->cpuSpeed = -1; + } else { + this->cpuSpeed = cpuSpeed; + } + } +} + +void Machine_scan(Machine* super) { + OpenBSDMachine* this = (OpenBSDMachine*) super; + + OpenBSDMachine_updateCPUcount(this); + OpenBSDMachine_scanMemoryInfo(this); + OpenBSDMachine_scanCPUTime(this); +} + +bool Machine_isCPUonline(const Machine* super, unsigned int id) { + assert(id < super->existingCPUs); + + const OpenBSDMachine* this = (const OpenBSDMachine*) super; + return this->cpuData[id + 1].online; +} diff --git a/openbsd/OpenBSDMachine.h b/openbsd/OpenBSDMachine.h new file mode 100644 index 00000000..51d6a750 --- /dev/null +++ b/openbsd/OpenBSDMachine.h @@ -0,0 +1,53 @@ +#ifndef HEADER_OpenBSDMachine +#define HEADER_OpenBSDMachine +/* +htop - OpenBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Machine.h" + + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; + + bool online; +} CPUData; + +typedef struct OpenBSDMachine_ { + Machine super; + kvm_t* kd; + + CPUData* cpuData; + + long fscale; + int cpuSpeed; + int pageSize; + int pageSizeKB; + +} OpenBSDMachine; + +#endif diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index c83eb543..2d86a9d4 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -17,7 +17,6 @@ in the source distribution for its full text. #include #include #include -#include #include #include #include @@ -29,169 +28,26 @@ in the source distribution for its full text. #include "ProcessList.h" #include "Settings.h" #include "XUtils.h" +#include "openbsd/OpenBSDMachine.h" #include "openbsd/OpenBSDProcess.h" -static long fscale; -static int pageSize; -static int pageSizeKB; - -static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) super; - Machine* host = super->host; - const int nmib[] = { CTL_HW, HW_NCPU }; - const int mib[] = { CTL_HW, HW_NCPUONLINE }; - int r; - unsigned int value; - size_t size; - bool change = false; - - size = sizeof(value); - r = sysctl(mib, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = 1; - } - - if (value != host->activeCPUs) { - host->activeCPUs = value; - change = true; - } - - size = sizeof(value); - r = sysctl(nmib, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = host->activeCPUs; - } - - if (value != host->existingCPUs) { - opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); - host->existingCPUs = value; - change = true; - } - - if (change) { - CPUData* dAvg = &opl->cpuData[0]; - memset(dAvg, '\0', sizeof(CPUData)); - dAvg->totalTime = 1; - dAvg->totalPeriod = 1; - dAvg->online = true; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - CPUData* d = &opl->cpuData[i + 1]; - memset(d, '\0', sizeof(CPUData)); - d->totalTime = 1; - d->totalPeriod = 1; - - const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i }; - struct cpustats cpu_stats; - - size = sizeof(cpu_stats); - if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) { - CRT_fatalError("ncmib sysctl call failed"); - } - d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE); - } - } -} - - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - const int fmib[] = { CTL_KERN, KERN_FSCALE }; - size_t size; - char errbuf[_POSIX2_LINE_MAX]; - - OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); - ProcessList* pl = (ProcessList*) opl; - ProcessList_init(pl, Class(OpenBSDProcess), host, pidMatchList); - - OpenBSDProcessList_updateCPUcount(pl); - - size = sizeof(fscale); - if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { - CRT_fatalError("fscale sysctl call failed"); - } - - if ((pageSize = sysconf(_SC_PAGESIZE)) == -1) - CRT_fatalError("pagesize sysconf call failed"); - pageSizeKB = pageSize / ONE_K; + OpenBSDProcessList* this = xCalloc(1, sizeof(OpenBSDProcessList)); + ProcessList* super = (ProcessList*) this; - opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - if (opl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - opl->cpuSpeed = -1; + ProcessList_init(super, Class(OpenBSDProcess), host, pidMatchList); - return pl; + return this; } -void ProcessList_delete(ProcessList* this) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) this; - - if (opl->kd) { - kvm_close(opl->kd); - } - - free(opl->cpuData); +void ProcessList_delete(ProcessList* super) { + OpenBSDProcessList* this = (OpenBSDProcessList*) super; - ProcessList_done(this); + ProcessList_done(super); free(this); } -static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { - Machine* host = pl->host; - const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; - struct uvmexp uvmexp; - size_t size_uvmexp = sizeof(uvmexp); - - if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { - CRT_fatalError("uvmexp sysctl call failed"); - } - - host->totalMem = uvmexp.npages * pageSizeKB; - host->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB; - - // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) - const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; - struct bcachestats bcstats; - size_t size_bcstats = sizeof(bcstats); - - if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { - CRT_fatalError("cannot get vfs.bcachestat"); - } - - host->cachedMem = bcstats.numbufpages * pageSizeKB; - - /* - * Copyright (c) 1994 Thorsten Lockert - * All rights reserved. - * - * Taken almost directly from OpenBSD's top(1) - * - * Originally released under a BSD-3 license - * Modified through htop developers applying GPL-2 - */ - int nswap = swapctl(SWAP_NSWAP, 0, 0); - if (nswap > 0) { - struct swapent swdev[nswap]; - int rnswap = swapctl(SWAP_STATS, swdev, nswap); - - /* Total things up */ - unsigned long long int total = 0, used = 0; - for (int i = 0; i < rnswap; i++) { - if (swdev[i].se_flags & SWF_ENABLE) { - used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); - total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); - } - } - - host->totalSwap = total; - host->usedSwap = used; - } else { - host->totalSwap = host->usedSwap = 0; - } -} - static void OpenBSDProcessList_updateCwd(const struct kinfo_proc* kproc, Process* proc) { const int mib[] = { CTL_KERN, KERN_PROC_CWD, kproc->p_pid }; char buffer[2048]; @@ -264,21 +120,22 @@ static void OpenBSDProcessList_updateProcessName(kvm_t* kd, const struct kinfo_p /* * Taken from OpenBSD's ps(1). */ -static double getpcpu(const struct kinfo_proc* kp) { - if (fscale == 0) +static double getpcpu(const OpenBSDMachine* ohost, const struct kinfo_proc* kp) { + if (ohost->fscale == 0) return 0.0; - return 100.0 * (double)kp->p_pctcpu / fscale; + return 100.0 * (double)kp->p_pctcpu / ohost->fscale; } static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { Machine* host = this->super.host; + OpenBSDMachine* ohost = (OpenBSDMachine*) host; const Settings* settings = host->settings; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; - const struct kinfo_proc* kprocs = kvm_getprocs(this->kd, KERN_PROC_KTHREAD | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &count); + const struct kinfo_proc* kprocs = kvm_getprocs(ohost->kd, KERN_PROC_KTHREAD | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; @@ -310,7 +167,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { Process_fillStarttimeBuffer(proc); ProcessList_add(&this->super, proc); - OpenBSDProcessList_updateProcessName(this->kd, kproc, proc); + OpenBSDProcessList_updateProcessName(ohost->kd, kproc, proc); if (settings->ss->flags & PROCESS_FLAG_CWD) { OpenBSDProcessList_updateCwd(kproc, proc); @@ -326,16 +183,16 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { } } else { if (settings->updateProcessNames) { - OpenBSDProcessList_updateProcessName(this->kd, kproc, proc); + OpenBSDProcessList_updateProcessName(ohost->kd, kproc, proc); } } fp->addr = kproc->p_addr; - proc->m_virt = kproc->p_vm_dsize * pageSizeKB; - proc->m_resident = kproc->p_vm_rssize * pageSizeKB; + proc->m_virt = kproc->p_vm_dsize * ohost->pageSizeKB; + proc->m_resident = kproc->p_vm_rssize * ohost->pageSizeKB; proc->percent_mem = proc->m_resident / (float)host->totalMem * 100.0F; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0F, host->activeCPUs * 100.0F); + proc->percent_cpu = CLAMP(getpcpu(ohost, kproc), 0.0F, host->activeCPUs * 100.0F); Process_updateCPUFieldWidths(proc->percent_cpu); proc->nice = kproc->p_nice - 20; @@ -379,122 +236,8 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { } } -static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) { - const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; - size_t length = sizeof(*times) * CPUSTATES; - if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { - CRT_fatalError("sysctl kern.cp_time2 failed"); - } -} - -static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { - unsigned long long totalTime = 0; - for (int i = 0; i < CPUSTATES; i++) { - totalTime += times[i]; - } - - unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; - - // XXX Not sure if CP_SPIN should be added to sysAllTime. - // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c - #ifdef CP_SPIN - sysAllTime += times[CP_SPIN]; - #endif - - cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); - cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); - cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); - cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); - cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); - #ifdef CP_SPIN - cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime); - #endif - cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); - cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); - - cpu->totalTime = totalTime; - cpu->userTime = times[CP_USER]; - cpu->niceTime = times[CP_NICE]; - cpu->sysTime = times[CP_SYS]; - cpu->sysAllTime = sysAllTime; - #ifdef CP_SPIN - cpu->spinTime = times[CP_SPIN]; - #endif - cpu->intrTime = times[CP_INTR]; - cpu->idleTime = times[CP_IDLE]; -} - -static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { - Machine* host = this->super.host; - u_int64_t kernelTimes[CPUSTATES] = {0}; - u_int64_t avg[CPUSTATES] = {0}; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - CPUData* cpu = &this->cpuData[i + 1]; - - if (!cpu->online) { - continue; - } - - getKernelCPUTimes(i, kernelTimes); - kernelCPUTimesToHtop(kernelTimes, cpu); - - avg[CP_USER] += cpu->userTime; - avg[CP_NICE] += cpu->niceTime; - avg[CP_SYS] += cpu->sysTime; - #ifdef CP_SPIN - avg[CP_SPIN] += cpu->spinTime; - #endif - avg[CP_INTR] += cpu->intrTime; - avg[CP_IDLE] += cpu->idleTime; - } - - for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= host->activeCPUs; - } - - kernelCPUTimesToHtop(avg, &this->cpuData[0]); - - { - const int mib[] = { CTL_HW, HW_CPUSPEED }; - int cpuSpeed; - size_t size = sizeof(cpuSpeed); - if (sysctl(mib, 2, &cpuSpeed, &size, NULL, 0) == -1) { - this->cpuSpeed = -1; - } else { - this->cpuSpeed = cpuSpeed; - } - } -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) super; - - OpenBSDProcessList_updateCPUcount(super); - OpenBSDProcessList_scanMemoryInfo(super); - OpenBSDProcessList_scanCPUTime(opl); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - - OpenBSDProcessList_scanProcs(opl); -} - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); +void ProcessList_goThroughEntries(ProcessList* super) { + OpenBSDProcessList* this = (OpenBSDProcessList*) super; - const OpenBSDProcessList* opl = (const OpenBSDProcessList*) host->pl; - return opl->cpuData[id + 1].online; + OpenBSDProcessList_scanProcs(this); } diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index 6ca2ba2c..8a03fecb 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -8,57 +8,14 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include -#include "Hashtable.h" #include "ProcessList.h" -#include "UsersTable.h" -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int niceTime; - unsigned long long int sysTime; - unsigned long long int sysAllTime; - unsigned long long int spinTime; - unsigned long long int intrTime; - unsigned long long int idleTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int nicePeriod; - unsigned long long int sysPeriod; - unsigned long long int sysAllPeriod; - unsigned long long int spinPeriod; - unsigned long long int intrPeriod; - unsigned long long int idlePeriod; - - bool online; -} CPUData; - typedef struct OpenBSDProcessList_ { ProcessList super; - kvm_t* kd; - - CPUData* cpuData; - int cpuSpeed; - } OpenBSDProcessList; - -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 03c36856..94e71928 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -35,7 +35,6 @@ in the source distribution for its full text. #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "Meter.h" -#include "ProcessList.h" #include "Settings.h" #include "SignalsPanel.h" #include "SwapMeter.h" @@ -43,8 +42,8 @@ in the source distribution for its full text. #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" +#include "openbsd/OpenBSDMachine.h" #include "openbsd/OpenBSDProcess.h" -#include "openbsd/OpenBSDProcessList.h" const ScreenDefaults Platform_defaultScreens[] = { @@ -182,8 +181,8 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const OpenBSDProcessList* pl = (const OpenBSDProcessList*) host->pl; - const CPUData* cpuData = &(pl->cpuData[cpu]); + const OpenBSDMachine* ohost = (const OpenBSDMachine*) host; + const CPUData* cpuData = &(ohost->cpuData[cpu]); double total; double totalPercent; double* v = this->values; @@ -217,7 +216,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_TEMPERATURE] = NAN; - v[CPU_METER_FREQUENCY] = (pl->cpuSpeed != -1) ? pl->cpuSpeed : NAN; + v[CPU_METER_FREQUENCY] = (ohost->cpuSpeed != -1) ? ohost->cpuSpeed : NAN; return totalPercent; } diff --git a/pcp/PCPMachine.c b/pcp/PCPMachine.c new file mode 100644 index 00000000..59e05624 --- /dev/null +++ b/pcp/PCPMachine.c @@ -0,0 +1,332 @@ +/* +htop - PCPProcessList.c +(C) 2014 Hisham H. Muhammad +(C) 2020-2023 htop dev team +(C) 2020-2023 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "pcp/PCPMachine.h" + +#include +#include +#include +#include +#include +#include + +#include "Macros.h" +#include "Machine.h" +#include "Object.h" +#include "Platform.h" +#include "Settings.h" +#include "XUtils.h" + +#include "pcp/PCPMetric.h" +#include "pcp/PCPProcess.h" + + +static void PCPMachine_updateCPUcount(PCPMachine* this) { + Machine* super = &this->super; + super->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM); + unsigned int cpus = Platform_getMaxCPU(); + if (cpus == super->existingCPUs) + return; + if (cpus == 0) + cpus = super->activeCPUs; + if (cpus <= 1) + cpus = super->activeCPUs = 1; + super->existingCPUs = cpus; + + free(this->percpu); + free(this->values); + + this->percpu = xCalloc(cpus, sizeof(pmAtomValue*)); + for (unsigned int i = 0; i < cpus; i++) + this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); + this->values = xCalloc(cpus, sizeof(pmAtomValue)); +} + +static void PCPMachine_updateMemoryInfo(Machine* host) { + unsigned long long int freeMem = 0; + unsigned long long int swapFreeMem = 0; + unsigned long long int sreclaimableMem = 0; + host->totalMem = host->usedMem = host->cachedMem = 0; + host->usedSwap = host->totalSwap = host->sharedMem = 0; + + pmAtomValue value; + if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) + host->totalMem = value.ull; + if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL) + freeMem = value.ull; + if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL) + host->buffersMem = value.ull; + if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL) + sreclaimableMem = value.ull; + if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL) + host->sharedMem = value.ull; + if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL) + host->cachedMem = value.ull + sreclaimableMem - host->sharedMem; + const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem; + host->usedMem = (host->totalMem >= usedDiff) ? + host->totalMem - usedDiff : host->totalMem - freeMem; + if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL) + host->availableMem = MINIMUM(value.ull, host->totalMem); + else + host->availableMem = freeMem; + if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL) + swapFreeMem = value.ull; + if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL) + host->totalSwap = value.ull; + if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL) + host->cachedSwap = value.ull; + host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap; +} + +/* make copies of previously sampled values to avoid overwrite */ +static inline void PCPMachine_backupCPUTime(pmAtomValue* values) { + /* the PERIOD fields (must) mirror the TIME fields */ + for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) { + values[metric + CPU_TOTAL_PERIOD] = values[metric]; + } +} + +static inline void PCPMachine_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) { + pmAtomValue* value; + + /* new value for period */ + value = &values[previous]; + if (latest->ull > value->ull) + value->ull = latest->ull - value->ull; + else + value->ull = 0; + + /* new value for time */ + value = &values[previous - CPU_TOTAL_PERIOD]; + value->ull = latest->ull; +} + +/* using copied sampled values and new values, calculate derivations */ +static void PCPMachine_deriveCPUTime(pmAtomValue* values) { + + pmAtomValue* usertime = &values[CPU_USER_TIME]; + pmAtomValue* guesttime = &values[CPU_GUEST_TIME]; + usertime->ull -= guesttime->ull; + + pmAtomValue* nicetime = &values[CPU_NICE_TIME]; + pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME]; + nicetime->ull -= guestnicetime->ull; + + pmAtomValue* idletime = &values[CPU_IDLE_TIME]; + pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME]; + pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME]; + idlealltime->ull = idletime->ull + iowaittime->ull; + + pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME]; + pmAtomValue* irqtime = &values[CPU_IRQ_TIME]; + pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME]; + pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME]; + systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull; + + pmAtomValue* virtalltime = &values[CPU_GUEST_TIME]; + virtalltime->ull = guesttime->ull + guestnicetime->ull; + + pmAtomValue* stealtime = &values[CPU_STEAL_TIME]; + pmAtomValue* totaltime = &values[CPU_TOTAL_TIME]; + totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull + + idlealltime->ull + stealtime->ull + virtalltime->ull; + + PCPMachine_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime); + PCPMachine_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime); + PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime); + PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime); + PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime); + PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime); + PCPMachine_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime); + PCPMachine_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime); + PCPMachine_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime); + PCPMachine_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime); + PCPMachine_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime); + PCPMachine_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime); +} + +static void PCPMachine_updateAllCPUTime(PCPMachine* this, PCPMetric metric, CPUMetric cpumetric) +{ + pmAtomValue* value = &this->cpu[cpumetric]; + if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL) + memset(value, 0, sizeof(pmAtomValue)); +} + +static void PCPMachine_updatePerCPUTime(PCPMachine* this, PCPMetric metric, CPUMetric cpumetric) +{ + int cpus = this->super.existingCPUs; + if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL) + memset(this->values, 0, cpus * sizeof(pmAtomValue)); + for (int i = 0; i < cpus; i++) + this->percpu[i][cpumetric].ull = this->values[i].ull; +} + +static void PCPMachine_updatePerCPUReal(PCPMachine* this, PCPMetric metric, CPUMetric cpumetric) +{ + int cpus = this->super.existingCPUs; + if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL) + memset(this->values, 0, cpus * sizeof(pmAtomValue)); + for (int i = 0; i < cpus; i++) + this->percpu[i][cpumetric].d = this->values[i].d; +} + +static inline void PCPMachine_scanZfsArcstats(PCPMachine* this) { + unsigned long long int dbufSize = 0; + unsigned long long int dnodeSize = 0; + unsigned long long int bonusSize = 0; + pmAtomValue value; + + memset(&this->zfs, 0, sizeof(ZfsArcStats)); + if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.anon = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64)) + this->zfs.min = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64)) + this->zfs.max = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64)) + bonusSize = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64)) + dbufSize = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64)) + dnodeSize = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.compressed = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.uncompressed = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.header = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.MFU = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.MRU = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.size = value.ull / ONE_K; + + this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K; + this->zfs.enabled = (this->zfs.size > 0); + this->zfs.isCompressed = (this->zfs.compressed > 0); +} + +static void PCPMachine_scan(PCPMachine* this) { + Machine* super = &this->super; + + PCPMachine_updateMemoryInfo(super); + PCPMachine_updateCPUcount(this); + + PCPMachine_backupCPUTime(this->cpu); + PCPMachine_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME); + PCPMachine_deriveCPUTime(this->cpu); + + for (unsigned int i = 0; i < super->existingCPUs; i++) + PCPMachine_backupCPUTime(this->percpu[i]); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME); + for (unsigned int i = 0; i < super->existingCPUs; i++) + PCPMachine_deriveCPUTime(this->percpu[i]); + + if (super->settings->showCPUFrequency) + PCPMachine_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY); + + PCPMachine_scanZfsArcstats(this); +} + +void Machine_scan(Machine* super) { + PCPMachine* host = (PCPMachine*) super; + const Settings* settings = super->settings; + uint32_t flags = settings->ss->flags; + bool flagged; + + for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++) + PCPMetric_enable(metric, true); + + flagged = settings->showCPUFrequency; + PCPMetric_enable(PCP_HINV_CPUCLOCK, flagged); + flagged = flags & PROCESS_FLAG_LINUX_CGROUP; + PCPMetric_enable(PCP_PROC_CGROUPS, flagged); + flagged = flags & PROCESS_FLAG_LINUX_OOM; + PCPMetric_enable(PCP_PROC_OOMSCORE, flagged); + flagged = flags & PROCESS_FLAG_LINUX_CTXT; + PCPMetric_enable(PCP_PROC_VCTXSW, flagged); + PCPMetric_enable(PCP_PROC_NVCTXSW, flagged); + flagged = flags & PROCESS_FLAG_LINUX_SECATTR; + PCPMetric_enable(PCP_PROC_LABELS, flagged); + flagged = flags & PROCESS_FLAG_LINUX_AUTOGROUP; + PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged); + PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged); + + /* Sample smaps metrics on every second pass to improve performance */ + host->smaps_flag = !!host->smaps_flag; + PCPMetric_enable(PCP_PROC_SMAPS_PSS, host->smaps_flag); + PCPMetric_enable(PCP_PROC_SMAPS_SWAP, host->smaps_flag); + PCPMetric_enable(PCP_PROC_SMAPS_SWAPPSS, host->smaps_flag); + + struct timeval timestamp; + if (PCPMetric_fetch(×tamp) != true) + return; + + double sample = host->timestamp; + host->timestamp = pmtimevalToReal(×tamp); + host->period = (host->timestamp - sample) * 100; + + PCPMachine_scan(host); +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + PCPMachine* this = xCalloc(1, sizeof(PCPMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + struct timeval timestamp; + gettimeofday(×tamp, NULL); + this->timestamp = pmtimevalToReal(×tamp); + + this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); + PCPMachine_updateCPUcount(this); + + return super; +} + +void Machine_delete(Machine* super) { + PCPMachine* this = (PCPMachine*) super; + Machine_done(super); + free(this->values); + for (unsigned int i = 0; i < super->existingCPUs; i++) + free(this->percpu[i]); + free(this->percpu); + free(this->cpu); + free(this); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + (void) host; + + pmAtomValue value; + if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32)) + return true; + return false; +} diff --git a/pcp/PCPMachine.h b/pcp/PCPMachine.h new file mode 100644 index 00000000..faca8efc --- /dev/null +++ b/pcp/PCPMachine.h @@ -0,0 +1,68 @@ +#ifndef HEADER_PCPMachine +#define HEADER_PCPMachine +/* +htop - PCPMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "UsersTable.h" + +#include "pcp/Platform.h" +#include "zfs/ZfsArcStats.h" + + +typedef enum CPUMetric_ { + CPU_TOTAL_TIME, + CPU_USER_TIME, + CPU_SYSTEM_TIME, + CPU_SYSTEM_ALL_TIME, + CPU_IDLE_ALL_TIME, + CPU_IDLE_TIME, + CPU_NICE_TIME, + CPU_IOWAIT_TIME, + CPU_IRQ_TIME, + CPU_SOFTIRQ_TIME, + CPU_STEAL_TIME, + CPU_GUEST_TIME, + CPU_GUESTNICE_TIME, + + CPU_TOTAL_PERIOD, + CPU_USER_PERIOD, + CPU_SYSTEM_PERIOD, + CPU_SYSTEM_ALL_PERIOD, + CPU_IDLE_ALL_PERIOD, + CPU_IDLE_PERIOD, + CPU_NICE_PERIOD, + CPU_IOWAIT_PERIOD, + CPU_IRQ_PERIOD, + CPU_SOFTIRQ_PERIOD, + CPU_STEAL_PERIOD, + CPU_GUEST_PERIOD, + CPU_GUESTNICE_PERIOD, + + CPU_FREQUENCY, + + CPU_METRIC_COUNT +} CPUMetric; + +typedef struct PCPMachine_ { + Machine super; + int smaps_flag; + double period; + double timestamp; /* previous sample timestamp */ + pmAtomValue* cpu; /* aggregate values for each metric */ + pmAtomValue** percpu; /* per-processor values for each metric */ + pmAtomValue* values; /* per-processor buffer for just one metric */ + ZfsArcStats zfs; +} PCPMachine; + +#endif diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 0e345fc0..c18d74be 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -26,68 +26,23 @@ in the source distribution for its full text. #include "Settings.h" #include "XUtils.h" +#include "pcp/PCPMachine.h" #include "pcp/PCPMetric.h" #include "pcp/PCPProcess.h" -static void PCPProcessList_updateCPUcount(PCPProcessList* this) { - Machine* host = this->super.host; - host->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM); - unsigned int cpus = Platform_getMaxCPU(); - if (cpus == host->existingCPUs) - return; - if (cpus == 0) - cpus = host->activeCPUs; - if (cpus <= 1) - cpus = host->activeCPUs = 1; - host->existingCPUs = cpus; - - free(this->percpu); - free(this->values); - - this->percpu = xCalloc(cpus, sizeof(pmAtomValue*)); - for (unsigned int i = 0; i < cpus; i++) - this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); - this->values = xCalloc(cpus, sizeof(pmAtomValue)); -} - -static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { - char* name = Hashtable_get(this->users, uid); - if (name) - return name; - - pmAtomValue value; - if (PCPMetric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) { - Hashtable_put(this->users, uid, value.cp); - name = value.cp; - } - return name; -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList)); ProcessList* super = &(this->super); ProcessList_init(super, Class(PCPProcess), host, pidMatchList); - struct timeval timestamp; - gettimeofday(×tamp, NULL); - this->timestamp = pmtimevalToReal(×tamp); - - this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); - PCPProcessList_updateCPUcount(this); - return super; } -void ProcessList_delete(ProcessList* pl) { - PCPProcessList* this = (PCPProcessList*) pl; - ProcessList_done(pl); - free(this->values); - for (unsigned int i = 0; i < pl->host->existingCPUs; i++) - free(this->percpu[i]); - free(this->percpu); - free(this->cpu); +void ProcessList_delete(ProcessList* super) { + PCPProcessList* this = (PCPProcessList*) super; + ProcessList_done(super); free(this); } @@ -143,6 +98,19 @@ static inline char Metric_instance_char(int metric, int pid, int offset, char fa return fallback; } +static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { + char* name = Hashtable_get(this->users, uid); + if (name) + return name; + + pmAtomValue value; + if (PCPMetric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) { + Hashtable_put(this->users, uid, value.cp); + name = value.cp; + } + return name; +} + static inline ProcessState PCPProcessList_getProcessState(char state) { switch (state) { case '?': return UNKNOWN; @@ -340,16 +308,17 @@ static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, } } -static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, struct timeval* tv) { +static bool PCPProcessList_updateProcesses(PCPProcessList* this) { ProcessList* pl = (ProcessList*) this; - Machine* host = pl->host; - const Settings* settings = host->settings; + PCPMachine* phost = (PCPMachine*) host; + const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; + uint32_t flags = settings->ss->flags; - unsigned long long now = tv->tv_sec * 1000LL + tv->tv_usec / 1000LL; + unsigned long long now = (unsigned long long)(phost->timestamp * 1000); int pid = -1, offset = -1; /* for every process ... */ @@ -387,12 +356,12 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, continue; } - if (settings->ss->flags & PROCESS_FLAG_IO) + if (flags & PROCESS_FLAG_IO) PCPProcessList_updateIO(pp, pid, offset, now); PCPProcessList_updateMemory(pp, pid, offset); - if ((settings->ss->flags & PROCESS_FLAG_LINUX_SMAPS) && + if ((flags & PROCESS_FLAG_LINUX_SMAPS) && (Process_isKernelThread(proc) == false)) { if (PCPMetric_enabled(PCP_PROC_SMAPS_PSS)) PCPProcessList_updateSmaps(pp, pid, offset); @@ -407,10 +376,10 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, if (tty_nr != proc->tty_nr) PCPProcessList_updateTTY(proc, pid, offset); - float percent_cpu = (pp->utime + pp->stime - lasttimes) / period * 100.0; + float percent_cpu = (pp->utime + pp->stime - lasttimes) / phost->period * 100.0; proc->percent_cpu = isnan(percent_cpu) ? 0.0 : CLAMP(percent_cpu, 0.0, host->activeCPUs * 100.0); - proc->percent_mem = proc->m_resident / (double)host->totalMem * 100.0; + proc->percent_mem = proc->m_resident / (double) host->totalMem * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); PCPProcessList_updateUsername(proc, pid, offset, host->usersTable); @@ -423,22 +392,22 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, PCPProcessList_updateCmdline(proc, pid, offset, command); } - if (settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP) + if (flags & PROCESS_FLAG_LINUX_CGROUP) PCPProcessList_readCGroups(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_OOM) + if (flags & PROCESS_FLAG_LINUX_OOM) PCPProcessList_readOomData(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_CTXT) + if (flags & PROCESS_FLAG_LINUX_CTXT) PCPProcessList_readCtxtData(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR) + if (flags & PROCESS_FLAG_LINUX_SECATTR) PCPProcessList_readSecattrData(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_CWD) + if (flags & PROCESS_FLAG_CWD) PCPProcessList_readCwd(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) + if (flags & PROCESS_FLAG_LINUX_AUTOGROUP) PCPProcessList_readAutogroup(pp, pid, offset); if (proc->state == ZOMBIE && !proc->cmdline && command[0]) { @@ -467,277 +436,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, return true; } -static void PCPProcessList_updateMemoryInfo(ProcessList* super) { - Machine* host = super->host; - unsigned long long int freeMem = 0; - unsigned long long int swapFreeMem = 0; - unsigned long long int sreclaimableMem = 0; - host->totalMem = host->usedMem = host->cachedMem = 0; - host->usedSwap = host->totalSwap = host->sharedMem = 0; - - pmAtomValue value; - if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) - host->totalMem = value.ull; - if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL) - freeMem = value.ull; - if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL) - host->buffersMem = value.ull; - if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL) - sreclaimableMem = value.ull; - if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL) - host->sharedMem = value.ull; - if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL) - host->cachedMem = value.ull + sreclaimableMem - host->sharedMem; - const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem; - host->usedMem = (host->totalMem >= usedDiff) ? - host->totalMem - usedDiff : host->totalMem - freeMem; - if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL) - host->availableMem = MINIMUM(value.ull, host->totalMem); - else - host->availableMem = freeMem; - if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL) - swapFreeMem = value.ull; - if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL) - host->totalSwap = value.ull; - if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL) - host->cachedSwap = value.ull; - host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap; -} - -/* make copies of previously sampled values to avoid overwrite */ -static inline void PCPProcessList_backupCPUTime(pmAtomValue* values) { - /* the PERIOD fields (must) mirror the TIME fields */ - for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) { - values[metric + CPU_TOTAL_PERIOD] = values[metric]; - } -} - -static inline void PCPProcessList_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) { - pmAtomValue* value; - - /* new value for period */ - value = &values[previous]; - if (latest->ull > value->ull) - value->ull = latest->ull - value->ull; - else - value->ull = 0; - - /* new value for time */ - value = &values[previous - CPU_TOTAL_PERIOD]; - value->ull = latest->ull; -} - -/* using copied sampled values and new values, calculate derivations */ -static void PCPProcessList_deriveCPUTime(pmAtomValue* values) { - - pmAtomValue* usertime = &values[CPU_USER_TIME]; - pmAtomValue* guesttime = &values[CPU_GUEST_TIME]; - usertime->ull -= guesttime->ull; - - pmAtomValue* nicetime = &values[CPU_NICE_TIME]; - pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME]; - nicetime->ull -= guestnicetime->ull; - - pmAtomValue* idletime = &values[CPU_IDLE_TIME]; - pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME]; - pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME]; - idlealltime->ull = idletime->ull + iowaittime->ull; - - pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME]; - pmAtomValue* irqtime = &values[CPU_IRQ_TIME]; - pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME]; - pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME]; - systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull; - - pmAtomValue* virtalltime = &values[CPU_GUEST_TIME]; - virtalltime->ull = guesttime->ull + guestnicetime->ull; - - pmAtomValue* stealtime = &values[CPU_STEAL_TIME]; - pmAtomValue* totaltime = &values[CPU_TOTAL_TIME]; - totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull + - idlealltime->ull + stealtime->ull + virtalltime->ull; - - PCPProcessList_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime); - PCPProcessList_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime); - PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime); - PCPProcessList_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime); -} - -static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) -{ - pmAtomValue* value = &this->cpu[cpumetric]; - if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL) - memset(value, 0, sizeof(pmAtomValue)); -} - -static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) -{ - int cpus = this->super.host->existingCPUs; - if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL) - memset(this->values, 0, cpus * sizeof(pmAtomValue)); - for (int i = 0; i < cpus; i++) - this->percpu[i][cpumetric].ull = this->values[i].ull; -} - -static void PCPProcessList_updatePerCPUReal(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) -{ - int cpus = this->super.host->existingCPUs; - if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL) - memset(this->values, 0, cpus * sizeof(pmAtomValue)); - for (int i = 0; i < cpus; i++) - this->percpu[i][cpumetric].d = this->values[i].d; -} - -static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) { - unsigned long long int dbufSize = 0; - unsigned long long int dnodeSize = 0; - unsigned long long int bonusSize = 0; - pmAtomValue value; - - memset(&this->zfs, 0, sizeof(ZfsArcStats)); - if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.anon = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64)) - this->zfs.min = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64)) - this->zfs.max = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64)) - bonusSize = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64)) - dbufSize = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64)) - dnodeSize = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.compressed = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.uncompressed = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.header = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.MFU = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.MRU = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.size = value.ull / ONE_K; - - this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K; - this->zfs.enabled = (this->zfs.size > 0); - this->zfs.isCompressed = (this->zfs.compressed > 0); -} - -static void PCPProcessList_updateHeader(ProcessList* super, const Settings* settings) { - Machine* host = super->host; - PCPProcessList_updateMemoryInfo(super); - +void ProcessList_goThroughEntries(ProcessList* super) { PCPProcessList* this = (PCPProcessList*) super; - PCPProcessList_updateCPUcount(this); - - PCPProcessList_backupCPUTime(this->cpu); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME); - PCPProcessList_deriveCPUTime(this->cpu); - - for (unsigned int i = 0; i < host->existingCPUs; i++) - PCPProcessList_backupCPUTime(this->percpu[i]); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME); - for (unsigned int i = 0; i < host->existingCPUs; i++) - PCPProcessList_deriveCPUTime(this->percpu[i]); - - if (settings->showCPUFrequency) - PCPProcessList_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY); - - PCPProcessList_scanZfsArcstats(this); -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - PCPProcessList* this = (PCPProcessList*) super; - Machine* host = super->host; - const Settings* settings = host->settings; - bool enabled = !pauseProcessUpdate; - - bool flagged = settings->showCPUFrequency; - PCPMetric_enable(PCP_HINV_CPUCLOCK, flagged); - - /* In pause mode do not sample per-process metric values at all */ - for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++) - PCPMetric_enable(metric, enabled); - - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP; - PCPMetric_enable(PCP_PROC_CGROUPS, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_OOM; - PCPMetric_enable(PCP_PROC_OOMSCORE, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CTXT; - PCPMetric_enable(PCP_PROC_VCTXSW, flagged && enabled); - PCPMetric_enable(PCP_PROC_NVCTXSW, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR; - PCPMetric_enable(PCP_PROC_LABELS, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP; - PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged && enabled); - PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged && enabled); - - /* Sample smaps metrics on every second pass to improve performance */ - static int smaps_flag; - smaps_flag = !!smaps_flag; - PCPMetric_enable(PCP_PROC_SMAPS_PSS, smaps_flag && enabled); - PCPMetric_enable(PCP_PROC_SMAPS_SWAP, smaps_flag && enabled); - PCPMetric_enable(PCP_PROC_SMAPS_SWAPPSS, smaps_flag && enabled); - - struct timeval timestamp; - if (PCPMetric_fetch(×tamp) != true) - return; - - double sample = this->timestamp; - this->timestamp = pmtimevalToReal(×tamp); - - PCPProcessList_updateHeader(super, settings); - - /* In pause mode only update global data for meters (CPU, memory, etc) */ - if (pauseProcessUpdate) - return; - - double period = (this->timestamp - sample) * 100; - PCPProcessList_updateProcesses(this, period, ×tamp); -} - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - (void) host; - - pmAtomValue value; - if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32)) - return true; - return false; + PCPProcessList_updateProcesses(this); } diff --git a/pcp/PCPProcessList.h b/pcp/PCPProcessList.h index 9bce9cd7..47a36026 100644 --- a/pcp/PCPProcessList.h +++ b/pcp/PCPProcessList.h @@ -17,62 +17,10 @@ in the source distribution for its full text. #include "UsersTable.h" #include "pcp/Platform.h" -#include "zfs/ZfsArcStats.h" -typedef enum CPUMetric_ { - CPU_TOTAL_TIME, - CPU_USER_TIME, - CPU_SYSTEM_TIME, - CPU_SYSTEM_ALL_TIME, - CPU_IDLE_ALL_TIME, - CPU_IDLE_TIME, - CPU_NICE_TIME, - CPU_IOWAIT_TIME, - CPU_IRQ_TIME, - CPU_SOFTIRQ_TIME, - CPU_STEAL_TIME, - CPU_GUEST_TIME, - CPU_GUESTNICE_TIME, - - CPU_TOTAL_PERIOD, - CPU_USER_PERIOD, - CPU_SYSTEM_PERIOD, - CPU_SYSTEM_ALL_PERIOD, - CPU_IDLE_ALL_PERIOD, - CPU_IDLE_PERIOD, - CPU_NICE_PERIOD, - CPU_IOWAIT_PERIOD, - CPU_IRQ_PERIOD, - CPU_SOFTIRQ_PERIOD, - CPU_STEAL_PERIOD, - CPU_GUEST_PERIOD, - CPU_GUESTNICE_PERIOD, - - CPU_FREQUENCY, - - CPU_METRIC_COUNT -} CPUMetric; - typedef struct PCPProcessList_ { ProcessList super; - double timestamp; /* previous sample timestamp */ - pmAtomValue* cpu; /* aggregate values for each metric */ - pmAtomValue** percpu; /* per-processor values for each metric */ - pmAtomValue* values; /* per-processor buffer for just one metric */ - ZfsArcStats zfs; } PCPProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* pl); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/pcp/Platform.c b/pcp/Platform.c index 181898aa..12e0f4d7 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -46,6 +46,7 @@ in the source distribution for its full text. #include "linux/ZramStats.h" #include "pcp/PCPDynamicColumn.h" #include "pcp/PCPDynamicMeter.h" +#include "pcp/PCPMachine.h" #include "pcp/PCPMetric.h" #include "pcp/PCPProcessList.h" #include "zfs/ZfsArcMeter.h" @@ -484,9 +485,7 @@ long long Platform_getBootTime(void) { return pcp->btime; } -static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { - Settings* settings = this->host->settings; - +static double Platform_setOneCPUValues(Meter* this, const Settings* settings, pmAtomValue* values) { unsigned long long value = values[CPU_TOTAL_PERIOD].ull; double total = (double) (value == 0 ? 1 : value); double percent; @@ -524,18 +523,19 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { } double Platform_setCPUValues(Meter* this, int cpu) { - const PCPProcessList* pl = (const PCPProcessList*) this->host->pl; + const PCPMachine* phost = (const PCPMachine*) this->host; + const Settings* settings = this->host->settings; + if (cpu <= 0) /* use aggregate values */ - return Platform_setOneCPUValues(this, pl->cpu); - return Platform_setOneCPUValues(this, pl->percpu[cpu - 1]); + return Platform_setOneCPUValues(this, settings, phost->cpu); + return Platform_setOneCPUValues(this, settings, phost->percpu[cpu - 1]); } void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - const ProcessList* pl = host->pl; - const PCPProcessList* ppl = (const PCPProcessList*) pl; + const PCPMachine* phost = (const PCPMachine*) host; - this->total = host->totalMem; + this->total = host->totalMem; this->values[MEMORY_METER_USED] = host->usedMem; this->values[MEMORY_METER_BUFFERS] = host->buffersMem; this->values[MEMORY_METER_SHARED] = host->sharedMem; @@ -543,11 +543,11 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; this->values[MEMORY_METER_AVAILABLE] = host->availableMem; - if (ppl->zfs.enabled != 0) { + if (phost->zfs.enabled != 0) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (ppl->zfs.size > ppl->zfs.min) - shrinkableSize = ppl->zfs.size - ppl->zfs.min; + if (phost->zfs.size > phost->zfs.min) + shrinkableSize = phost->zfs.size - phost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; @@ -595,15 +595,15 @@ void Platform_setZramValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const PCPProcessList* ppl = (const PCPProcessList*) this->host->pl; + const PCPMachine* phost = (const PCPMachine*) this->host; - ZfsArcMeter_readStats(this, &(ppl->zfs)); + ZfsArcMeter_readStats(this, &phost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { - const PCPProcessList* ppl = (const PCPProcessList*) this->host->pl; + const PCPMachine* phost = (const PCPMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(ppl->zfs)); + ZfsCompressedArcMeter_readStats(this, &phost->zfs); } void Platform_getHostname(char* buffer, size_t size) { diff --git a/solaris/Platform.c b/solaris/Platform.c index 8c88fc85..ad899492 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -34,10 +34,11 @@ in the source distribution for its full text. #include "HostnameMeter.h" #include "SysArchMeter.h" #include "UptimeMeter.h" + +#include "solaris/SolarisMachine.h" + #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" -#include "SolarisProcess.h" -#include "SolarisProcessList.h" const ScreenDefaults Platform_defaultScreens[] = { @@ -195,15 +196,15 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const SolarisProcessList* spl = (const SolarisProcessList*) host->pl; + const SolarisMachine* shost = (const SolarisMachine*) host; unsigned int cpus = host->existingCPUs; const CPUData* cpuData = NULL; if (cpus == 1) { // single CPU box has everything in spl->cpus[0] - cpuData = &(spl->cpus[0]); + cpuData = &(shost->cpus[0]); } else { - cpuData = &(spl->cpus[cpu]); + cpuData = &(shost->cpus[cpu]); } if (!cpuData->online) { @@ -216,7 +217,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (host->settings->detailedCPUTime) { + if (super->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -255,15 +256,15 @@ void Platform_setSwapValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->host->pl; + const SolarisMachine* shost = (SolarisMachine*) this->host; - ZfsArcMeter_readStats(this, &(spl->zfs)); + ZfsArcMeter_readStats(this, &(shost->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->host->pl; + const SolarisMachine* shost = (SolarisMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(spl->zfs)); + ZfsCompressedArcMeter_readStats(this, &(shost->zfs)); } static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr_t addr, const char* str) { diff --git a/solaris/SolarisMachine.c b/solaris/SolarisMachine.c new file mode 100644 index 00000000..9ce4ced2 --- /dev/null +++ b/solaris/SolarisMachine.c @@ -0,0 +1,332 @@ +/* +htop - SolarisMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2017,2018 Guy M. Broome +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + + +#include "solaris/SolarisMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "solaris/Platform.h" + + +static void SolarisMachine_updateCPUcount(SolarisMachine* this) { + Machine* super = &this->super; + long int s; + bool change = false; + + s = sysconf(_SC_NPROCESSORS_CONF); + if (s < 1) + CRT_fatalError("Cannot get existing CPU count by sysconf(_SC_NPROCESSORS_CONF)"); + + if (s != super->existingCPUs) { + if (s == 1) { + this->cpus = xRealloc(this->cpus, sizeof(CPUData)); + this->cpus[0].online = true; + } else { + this->cpus = xReallocArray(this->cpus, s + 1, sizeof(CPUData)); + this->cpus[0].online = true; /* average is always "online" */ + for (int i = 1; i < s + 1; i++) { + this->cpus[i].online = false; + } + } + + change = true; + super->existingCPUs = s; + } + + s = sysconf(_SC_NPROCESSORS_ONLN); + if (s < 1) + CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); + + if (s != super->activeCPUs) { + change = true; + hsuper->activeCPUs = s; + } + + if (change) { + kstat_close(this->kd); + this->kd = kstat_open(); + if (!this->kd) + CRT_fatalError("Cannot open kstat handle"); + } +} + + +static void SolarisMachine_scanCPUTime(SolarisMachine* this) { + Machine* super = &this->super; + unsigned int activeCPUs = super->activeCPUs; + unsigned int existingCPUs = super->existingCPUs; + kstat_t* cpuinfo = NULL; + kstat_named_t* idletime = NULL; + kstat_named_t* intrtime = NULL; + kstat_named_t* krnltime = NULL; + kstat_named_t* usertime = NULL; + kstat_named_t* cpu_freq = NULL; + double idlebuf = 0; + double intrbuf = 0; + double krnlbuf = 0; + double userbuf = 0; + int arrskip = 0; + + assert(existingCPUs > 0); + assert(this->kd); + + if (existingCPUs > 1) { + // Store values for the stats loop one extra element up in the array + // to leave room for the average to be calculated afterwards + arrskip++; + } + + // Calculate per-CPU statistics first + for (unsigned int i = 0; i < existingCPUs; i++) { + CPUData* cpuData = &(this->cpus[i + arrskip]); + + if ((cpuinfo = kstat_lookup_wrapper(this->kd, "cpu", i, "sys")) != NULL) { + cpuData->online = true; + if (kstat_read(this->kd, cpuinfo, NULL) != -1) { + idletime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_idle"); + intrtime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_intr"); + krnltime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_kernel"); + usertime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_user"); + } + } else { + cpuData->online = false; + continue; + } + + assert( (idletime != NULL) && (intrtime != NULL) + && (krnltime != NULL) && (usertime != NULL) ); + + if (super->settings->showCPUFrequency) { + if ((cpuinfo = kstat_lookup_wrapper(this->kd, "cpu_info", i, NULL)) != NULL) { + if (kstat_read(this->kd, cpuinfo, NULL) != -1) { + cpu_freq = kstat_data_lookup_wrapper(cpuinfo, "current_clock_Hz"); + } + } + + assert( cpu_freq != NULL ); + } + + uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle) + + (intrtime->value.ui64 - cpuData->lintr) + + (krnltime->value.ui64 - cpuData->lkrnl) + + (usertime->value.ui64 - cpuData->luser); + + // Calculate percentages of deltas since last reading + cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0; + cpuData->nicePercent = (double)0.0; // Not implemented on Solaris + cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0; + cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0; + cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; + cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0; + // Store current values to use for the next round of deltas + cpuData->luser = usertime->value.ui64; + cpuData->lkrnl = krnltime->value.ui64; + cpuData->lintr = intrtime->value.ui64; + cpuData->lidle = idletime->value.ui64; + // Add frequency in MHz + cpuData->frequency = super->settings->showCPUFrequency ? (double)cpu_freq->value.ui64 / 1E6 : NAN; + // Accumulate the current percentages into buffers for later average calculation + if (existingCPUs > 1) { + userbuf += cpuData->userPercent; + krnlbuf += cpuData->systemPercent; + intrbuf += cpuData->irqPercent; + idlebuf += cpuData->idlePercent; + } + } + + if (existingCPUs > 1) { + CPUData* cpuData = &(this->cpus[0]); + cpuData->userPercent = userbuf / activeCPUs; + cpuData->nicePercent = (double)0.0; // Not implemented on Solaris + cpuData->systemPercent = krnlbuf / activeCPUs; + cpuData->irqPercent = intrbuf / activeCPUs; + cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; + cpuData->idlePercent = idlebuf / activeCPUs; + } +} + +static void SolarisMachine_scanMemoryInfo(SolarisMachine* this) { + Machine* super = &this->super; + static kstat_t *meminfo = NULL; + int ksrphyserr = -1; + kstat_named_t *totalmem_pgs = NULL; + kstat_named_t *freemem_pgs = NULL; + kstat_named_t *pages = NULL; + struct swaptable *sl = NULL; + struct swapent *swapdev = NULL; + uint64_t totalswap = 0; + uint64_t totalfree = 0; + int nswap = 0; + char *spath = NULL; + char *spathbase = NULL; + + // Part 1 - physical memory + if (this->kd != NULL && meminfo == NULL) { + // Look up the kstat chain just once, it never changes + meminfo = kstat_lookup_wrapper(this->kd, "unix", 0, "system_pages"); + } + if (meminfo != NULL) { + ksrphyserr = kstat_read(this->kd, meminfo, NULL); + } + if (ksrphyserr != -1) { + totalmem_pgs = kstat_data_lookup_wrapper(meminfo, "physmem"); + freemem_pgs = kstat_data_lookup_wrapper(meminfo, "freemem"); + pages = kstat_data_lookup_wrapper(meminfo, "pagestotal"); + + super->totalMem = totalmem_pgs->value.ui64 * this->pageSizeKB; + if (super->totalMem > freemem_pgs->value.ui64 * this->pageSizeKB) { + super->usedMem = super->totalMem - freemem_pgs->value.ui64 * this->pageSizeKB; + } else { + super->usedMem = 0; // This can happen in non-global zone (in theory) + } + // Not sure how to implement this on Solaris - suggestions welcome! + super->cachedMem = 0; + // Not really "buffers" but the best Solaris analogue that I can find to + // "memory in use but not by programs or the kernel itself" + super->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * this->pageSizeKB; + } else { + // Fall back to basic sysconf if kstat isn't working + super->totalMem = sysconf(_SC_PHYS_PAGES) * this->pageSize; + super->buffersMem = 0; + super->cachedMem = 0; + super->usedMem = super->totalMem - (sysconf(_SC_AVPHYS_PAGES) * this->pageSize); + } + + // Part 2 - swap + nswap = swapctl(SC_GETNSWP, NULL); + if (nswap > 0) { + sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); + } + if (sl != NULL) { + spathbase = xMalloc( nswap * MAXPATHLEN ); + } + if (spathbase != NULL) { + spath = spathbase; + swapdev = sl->swt_ent; + for (int i = 0; i < nswap; i++, swapdev++) { + swapdev->ste_path = spath; + spath += MAXPATHLEN; + } + sl->swt_n = nswap; + } + nswap = swapctl(SC_LIST, sl); + if (nswap > 0) { + swapdev = sl->swt_ent; + for (int i = 0; i < nswap; i++, swapdev++) { + totalswap += swapdev->ste_pages; + totalfree += swapdev->ste_free; + } + } + free(spathbase); + free(sl); + super->totalSwap = totalswap * this->pageSizeKB; + super->usedSwap = super->totalSwap - (totalfree * this->pageSizeKB); +} + +static void SolarisMachine_scanZfsArcstats(SolarisMachine* this) { + kstat_named_t *cur_kstat = NULL; + kstat_t *arcstats = NULL; + int ksrphyserr = -1; + + if (this->kd != NULL) { + arcstats = kstat_lookup_wrapper(this->kd, "zfs", 0, "arcstats"); + } + if (arcstats != NULL) { + ksrphyserr = kstat_read(this->kd, arcstats, NULL); + } + if (ksrphyserr != -1) { + cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" ); + this->zfs.size = cur_kstat->value.ui64 / 1024; + this->zfs.enabled = this->zfs.size > 0 ? 1 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" ); + this->zfs.max = cur_kstat->value.ui64 / 1024; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" ); + this->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" ); + this->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" ); + this->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" ); + this->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" ); + this->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) { + this->zfs.compressed = cur_kstat->value.ui64 / 1024; + this->zfs.isCompressed = 1; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" ); + this->zfs.uncompressed = cur_kstat->value.ui64 / 1024; + } else { + this->zfs.isCompressed = 0; + } + } +} + +void Machine_scan(Machine* super) { + SolarisMachine* this = (SolarisMachine*) super; + + SolarisMachine_updateCPUcount(this); + SolarisMachine_scanCPUTime(this); + SolarisMachine_scanMemoryInfo(this); + SolarisMachine_scanZfsArcstats(this); +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + SolarisMachine* this = xCalloc(1, sizeof(SolarisMachine)); + Machine *super = &this->super; + + Machine_init(super, usersTable, userId); + + this->pageSize = sysconf(_SC_PAGESIZE); + if (this->pageSize == -1) + CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); + this->pageSizeKB = this->pageSize / 1024; + + SolarisMachine_updateCPUcount(this); + + return super; +} + +void Machine_delete(Machine* super) { + SolarisMachine* this = (SolarisMachine*) super; + + Machine_done(super); + + free(this->cpus); + if (this->kd) { + kstat_close(this->kd); + } + free(this); +} + +bool Machine_isCPUonline(const Machine* super, unsigned int id) { + assert(id < super->existingCPUs); + + const SolarisMachine* this = (const SolarisMachine*) super; + + return (super->existingCPUs == 1) ? true : this->cpus[id + 1].online; +} diff --git a/solaris/SolarisMachine.h b/solaris/SolarisMachine.h new file mode 100644 index 00000000..da091c6e --- /dev/null +++ b/solaris/SolarisMachine.h @@ -0,0 +1,59 @@ +#ifndef HEADER_SolarisMachine +#define HEADER_SolarisMachine +/* +htop - SolarisMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2017,2018 Guy M. Broome +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "UsersTable.h" + +#include "zfs/ZfsArcStats.h" + + +#define ZONE_ERRMSGLEN 1024 +extern char zone_errmsg[ZONE_ERRMSGLEN]; + +typedef struct CPUData_ { + double userPercent; + double nicePercent; + double systemPercent; + double irqPercent; + double idlePercent; + double systemAllPercent; + double frequency; + uint64_t luser; + uint64_t lkrnl; + uint64_t lintr; + uint64_t lidle; + bool online; +} CPUData; + +typedef struct SolarisMachine_ { + Machine super; + + kstat_ctl_t* kd; + CPUData* cpus; + + int pageSize; + int pageSizeKB; + + ZfsArcStats zfs; +} SolarisMachine; + +#endif diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index 84991d6e..e759b187 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -29,9 +29,6 @@ in the source distribution for its full text. #define GZONE "global " #define UZONE "unknown " -static int pageSize; -static int pageSizeKB; - static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) { char* zname; @@ -47,296 +44,18 @@ static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sp return zname; } -static void SolarisProcessList_updateCPUcount(ProcessList* super) { - Machine* host = super->host; - SolarisProcessList* spl = (SolarisProcessList*) super; - long int s; - bool change = false; - - s = sysconf(_SC_NPROCESSORS_CONF); - if (s < 1) - CRT_fatalError("Cannot get existing CPU count by sysconf(_SC_NPROCESSORS_CONF)"); - - if (s != host->existingCPUs) { - if (s == 1) { - spl->cpus = xRealloc(spl->cpus, sizeof(CPUData)); - spl->cpus[0].online = true; - } else { - spl->cpus = xReallocArray(spl->cpus, s + 1, sizeof(CPUData)); - spl->cpus[0].online = true; /* average is always "online" */ - for (int i = 1; i < s + 1; i++) { - spl->cpus[i].online = false; - } - } - - change = true; - host->existingCPUs = s; - } - - s = sysconf(_SC_NPROCESSORS_ONLN); - if (s < 1) - CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); - - if (s != host->activeCPUs) { - change = true; - host->activeCPUs = s; - } - - if (change) { - kstat_close(spl->kd); - spl->kd = kstat_open(); - if (!spl->kd) - CRT_fatalError("Cannot open kstat handle"); - } -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; - ProcessList_init(pl, Class(SolarisProcess), host, pidMatchList); - - spl->kd = kstat_open(); - if (!spl->kd) - CRT_fatalError("Cannot open kstat handle"); - pageSize = sysconf(_SC_PAGESIZE); - if (pageSize == -1) - CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); - pageSizeKB = pageSize / 1024; - - SolarisProcessList_updateCPUcount(pl); + ProcessList_init(pl, Class(SolarisProcess), host, pidMatchList); return pl; } -static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { - const SolarisProcessList* spl = (SolarisProcessList*) pl; - unsigned int activeCPUs = pl->host->activeCPUs; - unsigned int existingCPUs = pl->host->existingCPUs; - kstat_t* cpuinfo = NULL; - kstat_named_t* idletime = NULL; - kstat_named_t* intrtime = NULL; - kstat_named_t* krnltime = NULL; - kstat_named_t* usertime = NULL; - kstat_named_t* cpu_freq = NULL; - double idlebuf = 0; - double intrbuf = 0; - double krnlbuf = 0; - double userbuf = 0; - int arrskip = 0; - - assert(existingCPUs > 0); - assert(spl->kd); - - if (existingCPUs > 1) { - // Store values for the stats loop one extra element up in the array - // to leave room for the average to be calculated afterwards - arrskip++; - } - - // Calculate per-CPU statistics first - for (unsigned int i = 0; i < existingCPUs; i++) { - CPUData* cpuData = &(spl->cpus[i + arrskip]); - - if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu", i, "sys")) != NULL) { - cpuData->online = true; - if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { - idletime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_idle"); - intrtime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_intr"); - krnltime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_kernel"); - usertime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_user"); - } - } else { - cpuData->online = false; - continue; - } - - assert( (idletime != NULL) && (intrtime != NULL) - && (krnltime != NULL) && (usertime != NULL) ); - - if (pl->settings->showCPUFrequency) { - if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu_info", i, NULL)) != NULL) { - if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { - cpu_freq = kstat_data_lookup_wrapper(cpuinfo, "current_clock_Hz"); - } - } - - assert( cpu_freq != NULL ); - } - - uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle) - + (intrtime->value.ui64 - cpuData->lintr) - + (krnltime->value.ui64 - cpuData->lkrnl) - + (usertime->value.ui64 - cpuData->luser); - - // Calculate percentages of deltas since last reading - cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0; - cpuData->nicePercent = (double)0.0; // Not implemented on Solaris - cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0; - cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0; - cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; - cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0; - // Store current values to use for the next round of deltas - cpuData->luser = usertime->value.ui64; - cpuData->lkrnl = krnltime->value.ui64; - cpuData->lintr = intrtime->value.ui64; - cpuData->lidle = idletime->value.ui64; - // Add frequency in MHz - cpuData->frequency = pl->settings->showCPUFrequency ? (double)cpu_freq->value.ui64 / 1E6 : NAN; - // Accumulate the current percentages into buffers for later average calculation - if (existingCPUs > 1) { - userbuf += cpuData->userPercent; - krnlbuf += cpuData->systemPercent; - intrbuf += cpuData->irqPercent; - idlebuf += cpuData->idlePercent; - } - } - - if (existingCPUs > 1) { - CPUData* cpuData = &(spl->cpus[0]); - cpuData->userPercent = userbuf / activeCPUs; - cpuData->nicePercent = (double)0.0; // Not implemented on Solaris - cpuData->systemPercent = krnlbuf / activeCPUs; - cpuData->irqPercent = intrbuf / activeCPUs; - cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; - cpuData->idlePercent = idlebuf / activeCPUs; - } -} - -static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { - Machine* host = pl->host; - SolarisProcessList* spl = (SolarisProcessList*) pl; - - static kstat_t *meminfo = NULL; - int ksrphyserr = -1; - kstat_named_t *totalmem_pgs = NULL; - kstat_named_t *freemem_pgs = NULL; - kstat_named_t *pages = NULL; - struct swaptable *sl = NULL; - struct swapent *swapdev = NULL; - uint64_t totalswap = 0; - uint64_t totalfree = 0; - int nswap = 0; - char *spath = NULL; - char *spathbase = NULL; - - // Part 1 - physical memory - if (spl->kd != NULL && meminfo == NULL) { - // Look up the kstat chain just once, it never changes - meminfo = kstat_lookup_wrapper(spl->kd, "unix", 0, "system_pages"); - } - if (meminfo != NULL) { - ksrphyserr = kstat_read(spl->kd, meminfo, NULL); - } - if (ksrphyserr != -1) { - totalmem_pgs = kstat_data_lookup_wrapper(meminfo, "physmem"); - freemem_pgs = kstat_data_lookup_wrapper(meminfo, "freemem"); - pages = kstat_data_lookup_wrapper(meminfo, "pagestotal"); - - host->totalMem = totalmem_pgs->value.ui64 * pageSizeKB; - if (host->totalMem > freemem_pgs->value.ui64 * pageSizeKB) { - host->usedMem = host->totalMem - freemem_pgs->value.ui64 * pageSizeKB; - } else { - host->usedMem = 0; // This can happen in non-global zone (in theory) - } - // Not sure how to implement this on Solaris - suggestions welcome! - host->cachedMem = 0; - // Not really "buffers" but the best Solaris analogue that I can find to - // "memory in use but not by programs or the kernel itself" - host->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * pageSizeKB; - } else { - // Fall back to basic sysconf if kstat isn't working - host->totalMem = sysconf(_SC_PHYS_PAGES) * pageSize; - host->buffersMem = 0; - host->cachedMem = 0; - host->usedMem = host->totalMem - (sysconf(_SC_AVPHYS_PAGES) * pageSize); - } - - // Part 2 - swap - nswap = swapctl(SC_GETNSWP, NULL); - if (nswap > 0) { - sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); - } - if (sl != NULL) { - spathbase = xMalloc( nswap * MAXPATHLEN ); - } - if (spathbase != NULL) { - spath = spathbase; - swapdev = sl->swt_ent; - for (int i = 0; i < nswap; i++, swapdev++) { - swapdev->ste_path = spath; - spath += MAXPATHLEN; - } - sl->swt_n = nswap; - } - nswap = swapctl(SC_LIST, sl); - if (nswap > 0) { - swapdev = sl->swt_ent; - for (int i = 0; i < nswap; i++, swapdev++) { - totalswap += swapdev->ste_pages; - totalfree += swapdev->ste_free; - } - } - free(spathbase); - free(sl); - host->totalSwap = totalswap * pageSizeKB; - host->usedSwap = host->totalSwap - (totalfree * pageSizeKB); -} - -static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) { - SolarisProcessList* spl = (SolarisProcessList*) pl; - kstat_t *arcstats = NULL; - int ksrphyserr = -1; - kstat_named_t *cur_kstat = NULL; - - if (spl->kd != NULL) { - arcstats = kstat_lookup_wrapper(spl->kd, "zfs", 0, "arcstats"); - } - if (arcstats != NULL) { - ksrphyserr = kstat_read(spl->kd, arcstats, NULL); - } - if (ksrphyserr != -1) { - cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" ); - spl->zfs.size = cur_kstat->value.ui64 / 1024; - spl->zfs.enabled = spl->zfs.size > 0 ? 1 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" ); - spl->zfs.max = cur_kstat->value.ui64 / 1024; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" ); - spl->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" ); - spl->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" ); - spl->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" ); - spl->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" ); - spl->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) { - spl->zfs.compressed = cur_kstat->value.ui64 / 1024; - spl->zfs.isCompressed = 1; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" ); - spl->zfs.uncompressed = cur_kstat->value.ui64 / 1024; - } else { - spl->zfs.isCompressed = 0; - } - } -} - void ProcessList_delete(ProcessList* pl) { SolarisProcessList* spl = (SolarisProcessList*) pl; ProcessList_done(pl); - free(spl->cpus); - if (spl->kd) { - kstat_close(spl->kd); - } free(spl); } @@ -392,7 +111,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, // Setup process list ProcessList* pl = (ProcessList*) listptr; SolarisProcessList* spl = (SolarisProcessList*) listptr; - Machine* host = pl->super.host; + Machine* host = pl->host; id_t lwpid_real = _lwpsinfo->pr_lwpid; if (lwpid_real > 1023) { @@ -407,8 +126,9 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, getpid = lwpid; } - Process* proc = ProcessList_getProcess(pl, getpid, &preExisting, SolarisProcess_new); - SolarisProcess* sproc = (SolarisProcess*) proc; + Process* proc = ProcessList_getProcess(pl, getpid, &preExisting, SolarisProcess_new); + SolarisProcess* sproc = (SolarisProcess*) proc; + const Settings* settings = host->settings; // Common code pass 1 proc->show = false; @@ -455,7 +175,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, Process_updateComm(proc, _psinfo->pr_fname); Process_updateCmdline(proc, _psinfo->pr_psargs, 0, 0); - if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { SolarisProcessList_updateCwd(_psinfo->pr_pid, proc); } } @@ -479,7 +199,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, } // Update proc and thread counts based on settings - if (proc->isKernelThread && !pl->settings->hideKernelThreads) { + if (proc->isKernelThread && !settings->hideKernelThreads) { pl->kernelThreads += proc->nlwp; pl->totalTasks += proc->nlwp + 1; if (proc->state == RUNNING) { @@ -489,14 +209,14 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, if (proc->state == RUNNING) { pl->runningTasks++; } - if (pl->settings->hideUserlandThreads) { + if (settings->hideUserlandThreads) { pl->totalTasks++; } else { pl->userlandThreads += proc->nlwp; pl->totalTasks += proc->nlwp + 1; } } - proc->show = !(pl->settings->hideKernelThreads && proc->isKernelThread); + proc->show = !(settings->hideKernelThreads && proc->isKernelThread); } else { // We are not in the master LWP, so jump to the LWP handling code proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0; Process_updateCPUFieldWidths(proc->percent_cpu); @@ -512,10 +232,10 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, } // Top-level process only gets this for the representative LWP - if (proc->isKernelThread && !pl->settings->hideKernelThreads) { + if (proc->isKernelThread && !settings->hideKernelThreads) { proc->show = true; } - if (!proc->isKernelThread && !pl->settings->hideUserlandThreads) { + if (!proc->isKernelThread && !settings->hideUserlandThreads) { proc->show = true; } } // Top-level LWP or subordinate LWP @@ -540,35 +260,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, return 0; } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - SolarisProcessList_updateCPUcount(super); - SolarisProcessList_scanCPUTime(super); - SolarisProcessList_scanMemoryInfo(super); - SolarisProcessList_scanZfsArcstats(super); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - +void ProcessList_goThroughEntries(ProcessList* super) { super->kernelThreads = 1; proc_walk(&SolarisProcessList_walkproc, super, PR_WALK_LWP); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - const SolarisProcessList* spl = (const SolarisProcessList*) host->pl; - - return (super->host->existingCPUs == 1) ? true : spl->cpus[id + 1].online; -} diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index 7cb64167..d8280117 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -18,7 +18,6 @@ in the source distribution for its full text. #include #include #include -#include #include "Hashtable.h" #include "ProcessList.h" @@ -26,44 +25,9 @@ in the source distribution for its full text. #include "solaris/SolarisProcess.h" -#include "zfs/ZfsArcStats.h" - - -#define ZONE_ERRMSGLEN 1024 -extern char zone_errmsg[ZONE_ERRMSGLEN]; - -typedef struct CPUData_ { - double userPercent; - double nicePercent; - double systemPercent; - double irqPercent; - double idlePercent; - double systemAllPercent; - double frequency; - uint64_t luser; - uint64_t lkrnl; - uint64_t lintr; - uint64_t lidle; - bool online; -} CPUData; typedef struct SolarisProcessList_ { ProcessList super; - kstat_ctl_t* kd; - CPUData* cpus; - ZfsArcStats zfs; } SolarisProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* pl); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/unsupported/UnsupportedMachine.c b/unsupported/UnsupportedMachine.c new file mode 100644 index 00000000..a6635acc --- /dev/null +++ b/unsupported/UnsupportedMachine.c @@ -0,0 +1,56 @@ +/* +htop - UnsupportedMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "UnsupportedMachine.h" + +#include +#include + +#include "Machine.h" + + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + UnsupportedMachine* this = xCalloc(1, sizeof(UnsupportedMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + super->existingCPUs = 1; + super->activeCPUs = 1; + + return super; +} + +void Machine_delete(Machine* super) { + UnsupportedMachine* this = (UnsupportedMachine*) super; + Machine_done(super); + free(this); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + (void) host; (void) id; + + return true; +} + +void Machine_scan(Machine* super) { + super->existingCPUs = 1; + super->activeCPUs = 1; + + super->totalMem = 0; + super->usedMem = 0; + super->buffersMem = 0; + super->cachedMem = 0; + super->sharedMem = 0; + super->availableMem = 0; + + super->totalSwap = 0; + super->usedSwap = 0; + super->cachedSwap = 0; +} diff --git a/unsupported/UnsupportedMachine.h b/unsupported/UnsupportedMachine.h new file mode 100644 index 00000000..4ec760f1 --- /dev/null +++ b/unsupported/UnsupportedMachine.h @@ -0,0 +1,17 @@ +#ifndef HEADER_UnsupportedMachine +#define HEADER_UnsupportedMachine +/* +htop - UnsupportedMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Machine.h" + + +typedef struct UnsupportedMachine_ { + Machine super; +} UnsupportedMachine; + +#endif diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index e5b7af6d..e56f4978 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -16,10 +16,8 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); - ProcessList_init(this, Class(Process), host, pidMatchList); - host->existingCPUs = 1; - host->activeCPUs = 1; + ProcessList_init(this, Class(Process), host, pidMatchList); return this; } @@ -29,13 +27,7 @@ void ProcessList_delete(ProcessList* this) { free(this); } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - +void ProcessList_goThroughEntries(ProcessList* super) { bool preExisting = true; Process* proc; @@ -51,7 +43,8 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { Process_updateCmdline(proc, "", 0, 0); Process_updateExe(proc, "/path/to/executable"); - if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { + const Settings* settings = proc->host->settings; + if (settings->ss->flags & PROCESS_FLAG_CWD) { free_and_xStrdup(&proc->procCwd, "/current/working/directory"); } @@ -60,7 +53,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->state = RUNNING; proc->isKernelThread = false; proc->isUserlandThread = false; - proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */ + proc->show = true; /* Reflected in settings-> "hideXXX" really */ proc->pgrp = 0; proc->session = 0; proc->tty_nr = 0; @@ -90,21 +83,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (!preExisting) ProcessList_add(super, proc); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - (void) host; (void) id; - - return true; -} diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h index cdb0b601..96efdcd2 100644 --- a/unsupported/UnsupportedProcessList.h +++ b/unsupported/UnsupportedProcessList.h @@ -10,16 +10,8 @@ in the source distribution for its full text. #include "ProcessList.h" -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); +typedef struct UnsupportedProcessList_ { + ProcessList super; +} UnsupportedProcessList; #endif -- cgit v1.2.3