diff options
author | Daniel Lange <DLange@git.local> | 2024-01-10 11:17:08 +0100 |
---|---|---|
committer | Daniel Lange <DLange@git.local> | 2024-01-10 11:17:08 +0100 |
commit | e7372d18a1a661d8c3dba9f51e1f17b5f94171a7 (patch) | |
tree | e8270dd60ec096bee8157dbadf029e15ed584592 /darwin | |
parent | 937052b231259a47d881d539ad5748245ef55b99 (diff) | |
download | debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.tar.gz debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.tar.bz2 debian_htop-e7372d18a1a661d8c3dba9f51e1f17b5f94171a7.zip |
New upstream version 3.3.0
Diffstat (limited to 'darwin')
-rw-r--r-- | darwin/DarwinMachine.c | 119 | ||||
-rw-r--r-- | darwin/DarwinMachine.h | 28 | ||||
-rw-r--r-- | darwin/DarwinProcess.c | 70 | ||||
-rw-r--r-- | darwin/DarwinProcess.h | 8 | ||||
-rw-r--r-- | darwin/DarwinProcessList.c | 202 | ||||
-rw-r--r-- | darwin/DarwinProcessList.h | 39 | ||||
-rw-r--r-- | darwin/DarwinProcessTable.c | 126 | ||||
-rw-r--r-- | darwin/DarwinProcessTable.h | 22 | ||||
-rw-r--r-- | darwin/Platform.c | 232 | ||||
-rw-r--r-- | darwin/Platform.h | 34 | ||||
-rw-r--r-- | darwin/PlatformHelpers.c | 14 | ||||
-rw-r--r-- | darwin/PlatformHelpers.h | 2 |
12 files changed, 575 insertions, 321 deletions
diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c new file mode 100644 index 0000000..582d496 --- /dev/null +++ b/darwin/DarwinMachine.c @@ -0,0 +1,119 @@ +/* +htop - DarwinMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "darwin/DarwinMachine.h" + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/sysctl.h> + +#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 (!p) + return; + + if (!*p) + return; + + 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* this = (DarwinMachine*) super; + + DarwinMachine_freeCPULoadInfo(&this->prev_load); + + Machine_done(super); + free(this); +} + +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 0000000..3135b58 --- /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 <mach/mach_host.h> +#include <sys/sysctl.h> + +#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 d0204dd..1e315eb 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" @@ -51,10 +52,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, }, }; -Process* DarwinProcess_new(const Settings* settings) { +Process* DarwinProcess_new(const Machine* host) { DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess)); Object_setClass(this, Class(DarwinProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); this->utime = 0; this->stime = 0; @@ -71,18 +72,21 @@ void Process_delete(Object* cast) { free(this); } -static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) { - const DarwinProcess* dp = (const DarwinProcess*) this; +static void DarwinProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const DarwinProcess* dp = (const DarwinProcess*) super; + char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; - int n = sizeof(buffer) - 1; + size_t n = sizeof(buffer) - 1; + switch (field) { // add Platform-specific fields here case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break; default: - Process_writeField(this, str, field); + Process_writeField(&dp->super, str, field); return; } + RichString_appendWide(str, attr, buffer); } @@ -282,7 +286,7 @@ static char* DarwinProcess_getDevname(dev_t dev) { return NULL; } char buf[sizeof("/dev/") + MAXNAMLEN]; - char *name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN); + char* name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN); if (name) { return xStrdup(name); } @@ -291,13 +295,14 @@ static char* DarwinProcess_getDevname(dev_t dev) { void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { DarwinProcess* dp = (DarwinProcess*)proc; + const Settings* settings = proc->super.host->settings; const struct extern_proc* ep = &ps->kp_proc; /* UNSET HERE : * * processor - * user (set at ProcessList level) + * user (set at ProcessTable level) * nlwp * percent_cpu * percent_mem @@ -310,12 +315,12 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, /* First, the "immutable" parts */ if (!exists) { /* Set the PID/PGID/etc. */ - proc->pid = ep->p_pid; - proc->ppid = ps->kp_eproc.e_ppid; + Process_setPid(proc, ep->p_pid); + Process_setThreadGroup(proc, ep->p_pid); + Process_setParent(proc, ps->kp_eproc.e_ppid); proc->pgrp = ps->kp_eproc.e_pgid; proc->session = 0; /* TODO Get the session id */ proc->tpgid = ps->kp_eproc.e_tpgid; - proc->tgid = proc->pid; proc->isKernelThread = false; proc->isUserlandThread = false; dp->translated = ps->kp_proc.p_flag & P_TRANSLATED; @@ -328,7 +333,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, DarwinProcess_updateExe(ep->p_pid, proc); DarwinProcess_updateCmdLine(ps, proc); - if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { DarwinProcess_updateCwd(ep->p_pid, proc); } } @@ -341,7 +346,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, * To mitigate this we only fetch TTY information if the TTY * field is enabled in the settings. */ - if (proc->settings->ss->flags & PROCESS_FLAG_TTY) { + if (settings->ss->flags & PROCESS_FLAG_TTY) { proc->tty_name = DarwinProcess_getDevname(proc->tty_nr); if (!proc->tty_name) { /* devname failed: prevent us from calling it again */ @@ -357,13 +362,15 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, proc->state = (ep->p_stat == SZOMB) ? ZOMBIE : UNKNOWN; /* Make sure the updated flag is set */ - proc->updated = true; + proc->super.updated = true; } -void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double timeIntervalNS) { +void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable* dpt, double timeIntervalNS) { struct proc_taskinfo pti; - if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + if (sizeof(pti) == proc_pidinfo(Process_getPid(&proc->super), PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + const DarwinMachine* dhost = (const DarwinMachine*) proc->super.super.host; + uint64_t total_existing_time_ns = proc->stime + proc->utime; uint64_t user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user); @@ -385,15 +392,15 @@ 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; - dpl->super.kernelThreads += 0; /*pti.pti_threads_system;*/ - dpl->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/ - dpl->super.totalTasks += pti.pti_threadnum; - dpl->super.runningTasks += pti.pti_numrunning; + dpt->super.kernelThreads += 0; /*pti.pti_threads_system;*/ + dpt->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/ + dpt->super.totalTasks += pti.pti_threadnum; + dpt->super.runningTasks += pti.pti_numrunning; } } @@ -415,7 +422,7 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) { } task_t port; - ret = task_for_pid(mach_task_self(), proc->pid, &port); + ret = task_for_pid(mach_task_self(), Process_getPid(proc), &port); if (ret != KERN_SUCCESS) { dp->taskAccess = false; return; @@ -468,11 +475,18 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) { const ProcessClass DarwinProcess_class = { .super = { - .extends = Class(Process), - .display = Process_display, - .delete = Process_delete, - .compare = Process_compare + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = DarwinProcess_rowWriteField }, - .writeField = DarwinProcess_writeField, - .compareByKey = DarwinProcess_compareByKey, + .compareByKey = DarwinProcess_compareByKey }; diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h index bd17974..496b179 100644 --- a/darwin/DarwinProcess.h +++ b/darwin/DarwinProcess.h @@ -9,8 +9,8 @@ in the source distribution for its full text. #include <sys/sysctl.h> -#include "Settings.h" -#include "darwin/DarwinProcessList.h" +#include "Machine.h" +#include "darwin/DarwinProcessTable.h" #define PROCESS_FLAG_TTY 0x00000100 @@ -28,13 +28,13 @@ extern const ProcessClass DarwinProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* DarwinProcess_new(const Settings* settings); +Process* DarwinProcess_new(const Machine* settings); void Process_delete(Object* cast); void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists); -void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double timeIntervalNS); +void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable* dpt, double timeIntervalNS); /* * Scan threads for process state information. diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c deleted file mode 100644 index bd7821b..0000000 --- a/darwin/DarwinProcessList.c +++ /dev/null @@ -1,202 +0,0 @@ -/* -htop - DarwinProcessList.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/DarwinProcessList.h" - -#include <errno.h> -#include <libproc.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <utmpx.h> -#include <sys/mman.h> -#include <sys/sysctl.h> - -#include "CRT.h" -#include "ProcessList.h" -#include "darwin/DarwinProcess.h" -#include "darwin/Platform.h" -#include "darwin/PlatformHelpers.h" -#include "generic/openzfs_sysctl.h" -#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; - - for (int retry = 3; retry > 0; retry--) { - size_t size = 0; - if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) { - CRT_fatalError("Unable to get size of kproc_infos"); - } - - processes = xRealloc(processes, size); - - if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) { - *count = size / sizeof(struct kinfo_proc); - return processes; - } - - if (errno != ENOMEM) - break; - } - - CRT_fatalError("Unable to get kinfo_procs"); -} - -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { - DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); - - ProcessList_init(&this->super, Class(DarwinProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); - - /* Initialize the CPU information */ - this->super.activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); - // TODO: support offline CPUs and hot swapping - this->super.existingCPUs = this->super.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); - - this->super.kernelThreads = 0; - this->super.userlandThreads = 0; - this->super.totalTasks = 0; - this->super.runningTasks = 0; - - return &this->super; -} - -void ProcessList_delete(ProcessList* this) { - ProcessList_done(this); - free(this); -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - 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 < dpl->super.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]; - } - } - - const double time_interval_ns = Platform_schedulerTicksToNanoseconds(dpl->global_diff) / (double) dpl->super.activeCPUs; - - /* Clear the thread counts */ - super->kernelThreads = 0; - super->userlandThreads = 0; - super->totalTasks = 0; - super->runningTasks = 0; - - /* We use kinfo_procs for initial data since : - * - * 1) They always succeed. - * 2) The contain the basic information. - * - * We attempt to fill-in additional information with libproc. - */ - ps = ProcessList_getKInfoProcs(&count); - - for (size_t i = 0; i < count; ++i) { - proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new); - - DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting); - DarwinProcess_setFromLibprocPidinfo(proc, dpl, time_interval_ns); - - if (proc->super.st_uid != ps[i].kp_eproc.e_ucred.cr_uid) { - proc->super.st_uid = ps[i].kp_eproc.e_ucred.cr_uid; - proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid); - } - - // Disabled for High Sierra due to bug in macOS High Sierra - bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); - - if (isScanThreadSupported) { - DarwinProcess_scanThreads(proc); - } - - super->totalTasks += 1; - - if (!preExisting) { - ProcessList_add(super, &proc->super); - } - } - - free(ps); -} - -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) super; (void) id; - - return true; -} diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h deleted file mode 100644 index 393e656..0000000 --- a/darwin/DarwinProcessList.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HEADER_DarwinProcessList -#define HEADER_DarwinProcessList -/* -htop - DarwinProcessList.h -(C) 2014 Hisham H. Muhammad -Released under the GNU GPLv2+, see the COPYING file -in the source distribution for its full text. -*/ - -#include <mach/mach_host.h> -#include <sys/sysctl.h> - -#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(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); - -#endif diff --git a/darwin/DarwinProcessTable.c b/darwin/DarwinProcessTable.c new file mode 100644 index 0000000..850b503 --- /dev/null +++ b/darwin/DarwinProcessTable.c @@ -0,0 +1,126 @@ +/* +htop - DarwinProcessTable.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/DarwinProcessTable.h" + +#include <errno.h> +#include <libproc.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utmpx.h> +#include <sys/mman.h> +#include <sys/sysctl.h> + +#include "CRT.h" +#include "ProcessTable.h" +#include "darwin/DarwinMachine.h" +#include "darwin/DarwinProcess.h" +#include "darwin/Platform.h" +#include "darwin/PlatformHelpers.h" +#include "generic/openzfs_sysctl.h" +#include "zfs/ZfsArcStats.h" + + +static struct kinfo_proc* ProcessTable_getKInfoProcs(size_t* count) { + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + struct kinfo_proc* processes = NULL; + + for (unsigned int retry = 0; retry < 4; retry++) { + size_t size = 0; + if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) { + CRT_fatalError("Unable to get size of kproc_infos"); + } + + size += 16 * retry * retry * sizeof(struct kinfo_proc); + processes = xRealloc(processes, size); + + if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) { + *count = size / sizeof(struct kinfo_proc); + return processes; + } + + if (errno != ENOMEM) + break; + } + + CRT_fatalError("Unable to get kinfo_procs"); +} + +ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) { + DarwinProcessTable* this = xCalloc(1, sizeof(DarwinProcessTable)); + Object_setClass(this, Class(ProcessTable)); + + ProcessTable* super = &this->super; + ProcessTable_init(super, Class(DarwinProcess), host, pidMatchList); + + return super; +} + +void ProcessTable_delete(Object* cast) { + DarwinProcessTable* this = (DarwinProcessTable*) cast; + ProcessTable_done(&this->super); + free(this); +} + +void ProcessTable_goThroughEntries(ProcessTable* super) { + const Machine* host = super->super.host; + const DarwinMachine* dhost = (const DarwinMachine*) host; + DarwinProcessTable* dpt = (DarwinProcessTable*) super; + bool preExisting = true; + struct kinfo_proc* ps; + size_t count; + DarwinProcess* proc; + + /* Get the time difference */ + dpt->global_diff = 0; + for (unsigned int i = 0; i < host->existingCPUs; ++i) { + for (size_t j = 0; j < CPU_STATE_MAX; ++j) { + dpt->global_diff += dhost->curr_load[i].cpu_ticks[j] - dhost->prev_load[i].cpu_ticks[j]; + } + } + + const double time_interval_ns = Platform_schedulerTicksToNanoseconds(dpt->global_diff) / (double) host->activeCPUs; + + /* We use kinfo_procs for initial data since : + * + * 1) They always succeed. + * 2) They contain the basic information. + * + * We attempt to fill-in additional information with libproc. + */ + ps = ProcessTable_getKInfoProcs(&count); + + for (size_t i = 0; i < count; ++i) { + proc = (DarwinProcess*)ProcessTable_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new); + + DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting); + DarwinProcess_setFromLibprocPidinfo(proc, dpt, time_interval_ns); + + if (proc->super.st_uid != ps[i].kp_eproc.e_ucred.cr_uid) { + proc->super.st_uid = ps[i].kp_eproc.e_ucred.cr_uid; + proc->super.user = UsersTable_getRef(host->usersTable, proc->super.st_uid); + } + + // Disabled for High Sierra due to bug in macOS High Sierra + bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); + + if (isScanThreadSupported) { + DarwinProcess_scanThreads(proc); + } + + super->totalTasks += 1; + + if (!preExisting) { + ProcessTable_add(super, &proc->super); + } + } + + free(ps); +} diff --git a/darwin/DarwinProcessTable.h b/darwin/DarwinProcessTable.h new file mode 100644 index 0000000..7467bfd --- /dev/null +++ b/darwin/DarwinProcessTable.h @@ -0,0 +1,22 @@ +#ifndef HEADER_DarwinProcessTable +#define HEADER_DarwinProcessTable +/* +htop - DarwinProcessTable.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include <mach/mach_host.h> +#include <sys/sysctl.h> + +#include "ProcessTable.h" + + +typedef struct DarwinProcessTable_ { + ProcessTable super; + + uint64_t global_diff; +} DarwinProcessTable; + +#endif diff --git a/darwin/Platform.c b/darwin/Platform.c index 4b34d88..387910e 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -14,16 +14,30 @@ in the source distribution for its full text. #include <math.h> #include <stdlib.h> #include <unistd.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/route.h> +#include <sys/socket.h> +#include <sys/_types/_mach_port_t.h> + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFNumber.h> #include <CoreFoundation/CFString.h> #include <CoreFoundation/CoreFoundation.h> + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOTypes.h> #include <IOKit/ps/IOPowerSources.h> #include <IOKit/ps/IOPSKeys.h> +#include <IOKit/storage/IOBlockStorageDriver.h> #include "ClockMeter.h" #include "CPUMeter.h" #include "CRT.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -34,8 +48,9 @@ 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" #include "zfs/ZfsCompressedArcMeter.h" @@ -126,6 +141,9 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs8Meter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, + &DiskIOMeter_class, + &NetworkIOMeter_class, + &FileDescriptorMeter_class, &BlankMeter_class, NULL }; @@ -134,6 +152,9 @@ static double Platform_nanosecondsPerMachTick = 1.0; static double Platform_nanosecondsPerSchedulerTick = -1; +static bool iokit_available = false; +static mach_port_t iokit_port; // the mach port used to initiate communication with IOKit + bool Platform_init(void) { Platform_nanosecondsPerMachTick = Platform_calculateNanosecondsPerMachTick(); @@ -148,6 +169,17 @@ bool Platform_init(void) { const double nanos_per_sec = 1e9; Platform_nanosecondsPerSchedulerTick = nanos_per_sec / scheduler_ticks_per_sec; + // Since macOS 12.0, IOMasterPort is deprecated, and one should use IOMainPort instead + #if defined(HAVE_DECL_IOMAINPORT) && HAVE_DECL_IOMAINPORT + if (!IOMainPort(bootstrap_port, &iokit_port)) { + iokit_available = true; + } + #elif defined(HAVE_DECL_IOMASTERPORT) && HAVE_DECL_IOMASTERPORT + if (!IOMasterPort(bootstrap_port, &iokit_port)) { + iokit_available = true; + } + #endif + return true; } @@ -172,7 +204,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -200,19 +232,19 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { } } -int Platform_getMaxPid() { +pid_t Platform_getMaxPid(void) { /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */ return 99999; } static double Platform_setCPUAverageValues(Meter* mtr) { - const ProcessList* dpl = mtr->pl; - unsigned int activeCPUs = dpl->activeCPUs; + const Machine* host = mtr->host; + unsigned int activeCPUs = host->activeCPUs; double sumNice = 0.0; double sumNormal = 0.0; double sumKernel = 0.0; double sumPercent = 0.0; - for (unsigned int i = 1; i <= dpl->existingCPUs; i++) { + for (unsigned int i = 1; i <= host->existingCPUs; i++) { sumPercent += Platform_setCPUValues(mtr, i); sumNice += mtr->values[CPU_METER_NICE]; sumNormal += mtr->values[CPU_METER_NORMAL]; @@ -230,9 +262,9 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { return Platform_setCPUAverageValues(mtr); } - const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->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 */ @@ -259,16 +291,17 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { } void Platform_setMemoryValues(Meter* mtr) { - const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->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->values[0] = (double)(vm->active_count + vm->wire_count) * page_K; - mtr->values[1] = (double)vm->purgeable_count * page_K; - // mtr->values[2] = "shared memory, like tmpfs and shm" - mtr->values[3] = (double)vm->inactive_count * page_K; - // mtr->values[4] = "available memory" + 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_SHARED] = "shared memory, like tmpfs and shm" + // mtr->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" + mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; + mtr->values[MEMORY_METER_CACHE] = (double)vm->inactive_count * page_K; + // mtr->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* mtr) { @@ -278,19 +311,21 @@ void Platform_setSwapValues(Meter* mtr) { sysctl(mib, 2, &swapused, &swlen, NULL, 0); mtr->total = swapused.xsu_total / 1024; - mtr->values[0] = swapused.xsu_used / 1024; + mtr->values[SWAP_METER_USED] = swapused.xsu_used / 1024; + // 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" } void Platform_setZfsArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->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->pl; + const DarwinMachine* dhost = (const DarwinMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(dpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &dhost->zfs); } char* Platform_getProcessEnv(pid_t pid) { @@ -344,27 +379,158 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { +FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; - (void)inode; return NULL; } -FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { - (void)pid; - return NULL; +void Platform_getFileDescriptors(double* used, double* max) { + Generic_getFileDescriptors_sysctl(used, max); } bool Platform_getDiskIO(DiskIOData* data) { - // TODO - (void)data; - return false; + if (!iokit_available) + return false; + + io_iterator_t drive_list; + + /* Get the list of all drives */ + if (IOServiceGetMatchingServices(iokit_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list)) + return false; + + unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0; + + io_registry_entry_t drive; + while ((drive = IOIteratorNext(drive_list)) != 0) { + CFMutableDictionaryRef properties_tmp = NULL; + + /* Get the properties of this drive */ + if (IORegistryEntryCreateCFProperties(drive, &properties_tmp, kCFAllocatorDefault, 0)) { + IOObjectRelease(drive); + IOObjectRelease(drive_list); + return false; + } + + if (!properties_tmp) { + IOObjectRelease(drive); + continue; + } + + CFDictionaryRef properties = properties_tmp; + + /* Get the statistics of this drive */ + CFDictionaryRef statistics = (CFDictionaryRef) CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)); + + if (!statistics) { + CFRelease(properties); + IOObjectRelease(drive); + continue; + } + + CFNumberRef number; + unsigned long long int value; + + /* Get bytes read */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + read_sum += value; + } + + /* Get bytes written */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + write_sum += value; + } + + /* Get total read time (in ns) */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + timeSpend_sum += value; + } + + /* Get total write time (in ns) */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + timeSpend_sum += value; + } + + CFRelease(properties); + IOObjectRelease(drive); + } + + data->totalBytesRead = read_sum; + data->totalBytesWritten = write_sum; + data->totalMsTimeSpend = timeSpend_sum / 1e6; /* Convert from ns to ms */ + + if (drive_list) + IOObjectRelease(drive_list); + + return true; } +/* Caution: Given that interfaces are dynamic, and it is not possible to get statistics on interfaces that no longer exist, + if some interface disappears between the time of two samples, the values of the second sample may be lower than those of + the first one. */ bool Platform_getNetworkIO(NetworkIOData* data) { - // TODO - (void)data; - return false; + int mib[6] = {CTL_NET, + PF_ROUTE, /* routing messages */ + 0, /* protocal number, currently always 0 */ + 0, /* select all address families */ + NET_RT_IFLIST2, /* interface list with addresses */ + 0}; + + for (size_t retry = 0; retry < 4; retry++) { + size_t len = 0; + + /* Determine len */ + if (sysctl(mib, ARRAYSIZE(mib), NULL, &len, NULL, 0) < 0 || len == 0) + return false; + + len += 16 * retry * retry * sizeof(struct if_msghdr2); + char *buf = xMalloc(len); + + if (sysctl(mib, ARRAYSIZE(mib), buf, &len, NULL, 0) < 0) { + free(buf); + if (errno == ENOMEM && retry < 3) + continue; + else + return false; + } + + uint64_t bytesReceived_sum = 0, packetsReceived_sum = 0, bytesTransmitted_sum = 0, packetsTransmitted_sum = 0; + + for (char *next = buf; next < buf + len;) { + void *tmp = (void*) next; + struct if_msghdr *ifm = (struct if_msghdr*) tmp; + + next += ifm->ifm_msglen; + + if (ifm->ifm_type != RTM_IFINFO2) + continue; + + struct if_msghdr2 *ifm2 = (struct if_msghdr2*) ifm; + + if (ifm2->ifm_data.ifi_type != IFT_LOOP) { /* do not count loopback traffic */ + bytesReceived_sum += ifm2->ifm_data.ifi_ibytes; + packetsReceived_sum += ifm2->ifm_data.ifi_ipackets; + bytesTransmitted_sum += ifm2->ifm_data.ifi_obytes; + packetsTransmitted_sum += ifm2->ifm_data.ifi_opackets; + } + } + + data->bytesReceived = bytesReceived_sum; + data->packetsReceived = packetsReceived_sum; + data->bytesTransmitted = bytesTransmitted_sum; + data->packetsTransmitted = packetsTransmitted_sum; + + free(buf); + } + + return true; } void Platform_getBattery(double* percent, ACPresence* isOnAC) { diff --git a/darwin/Platform.h b/darwin/Platform.h index 4f8e7c9..f67db8f 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -54,7 +54,7 @@ int Platform_getUptime(void); void Platform_getLoadAverage(double* one, double* five, double* fifteen); -int Platform_getMaxPid(void); +pid_t Platform_getMaxPid(void); double Platform_setCPUValues(Meter* mtr, unsigned int cpu); @@ -68,10 +68,10 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); @@ -100,7 +100,9 @@ static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) void Platform_gettime_monotonic(uint64_t* msec); -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -110,12 +112,30 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) { + return NULL; +} + +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} + +static inline Hashtable* Platform_dynamicScreens(void) { + return NULL; +} + +static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { } + +static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { } + +static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { } -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { } #endif diff --git a/darwin/PlatformHelpers.c b/darwin/PlatformHelpers.c index bde9068..a4ea82b 100644 --- a/darwin/PlatformHelpers.c +++ b/darwin/PlatformHelpers.c @@ -58,7 +58,7 @@ bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upp && Platform_CompareKernelVersion(upperBound) < 0; } -void Platform_getCPUBrandString(char *cpuBrandString, size_t cpuBrandStringSize) { +void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize) { if (sysctlbyname("machdep.cpu.brand_string", cpuBrandString, &cpuBrandStringSize, NULL, 0) == -1) { fprintf(stderr, "WARN: Unable to determine the CPU brand string.\n" @@ -69,12 +69,13 @@ void Platform_getCPUBrandString(char *cpuBrandString, size_t cpuBrandStringSize) } // Adapted from https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment -bool Platform_isRunningTranslated() { +bool Platform_isRunningTranslated(void) { int ret = 0; size_t size = sizeof(ret); errno = 0; if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) { - if (errno == ENOENT) return false; + if (errno == ENOENT) + return false; fprintf(stderr, "WARN: Could not determine if this process was running in a translation environment like Rosetta 2.\n" @@ -86,7 +87,7 @@ bool Platform_isRunningTranslated() { return ret; } -double Platform_calculateNanosecondsPerMachTick() { +double Platform_calculateNanosecondsPerMachTick(void) { // Check if we can determine the timebase used on this system. // If the API is unavailable assume we get our timebase in nanoseconds. #ifndef HAVE_MACH_TIMEBASE_INFO @@ -102,9 +103,8 @@ double Platform_calculateNanosecondsPerMachTick() { * the "Apple M1" chip specifically when running under Rosetta 2. */ - size_t cpuBrandStringSize = 1024; - char cpuBrandString[cpuBrandStringSize]; - Platform_getCPUBrandString(cpuBrandString, cpuBrandStringSize); + char cpuBrandString[1024] = ""; + Platform_getCPUBrandString(cpuBrandString, sizeof(cpuBrandString)); bool isRunningUnderRosetta2 = Platform_isRunningTranslated(); diff --git a/darwin/PlatformHelpers.h b/darwin/PlatformHelpers.h index 07f3fe2..45aea1a 100644 --- a/darwin/PlatformHelpers.h +++ b/darwin/PlatformHelpers.h @@ -31,7 +31,7 @@ bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upp double Platform_calculateNanosecondsPerMachTick(void); -void Platform_getCPUBrandString(char *cpuBrandString, size_t cpuBrandStringSize); +void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize); bool Platform_isRunningTranslated(void); |