From 5b4d63d1be3a6dc1a8fd646e418ccf585ea85bc8 Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Fri, 20 Aug 2021 17:52:07 -0400 Subject: [PATCH] Fix macOS CPU time calculations --- darwin/DarwinProcess.c | 36 +++++++++++++++++++++++++++--------- darwin/DarwinProcess.h | 2 +- darwin/DarwinProcessList.c | 10 ++++++---- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 2bd6ec71..a6059877 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -269,6 +269,20 @@ ERROR_A: Process_updateCmdline(proc, k->kp_proc.p_comm, 0, strlen(k->kp_proc.p_comm)); } +// Converts ticks in the Mach "timebase" to nanoseconds. +// See `mach_timebase_info`, as used to define the `Platform_timebaseToNS` constant. +static uint64_t machTicksToNanoseconds(uint64_t schedulerTicks) { + double nanoseconds_per_mach_tick = Platform_timebaseToNS; + return (uint64_t) (nanoseconds_per_mach_tick * (double) schedulerTicks); +} + +// Converts nanoseconds to hundreths of a second (centiseconds) as needed by the "time" field of the Process struct. +static long long int nanosecondsToCentiseconds(uint64_t nanoseconds) { + uint64_t centiseconds_per_second = 100; + uint64_t nanoseconds_per_second = 1e9; + return nanoseconds / nanoseconds_per_second * centiseconds_per_second; +} + void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { DarwinProcess* dp = (DarwinProcess*)proc; @@ -330,21 +344,25 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, proc->updated = true; } -void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval) { +void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval_ns) { struct proc_taskinfo pti; if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { - uint64_t total_existing_time = proc->stime + proc->utime; - uint64_t total_current_time = pti.pti_total_system + pti.pti_total_user; + uint64_t total_existing_time_ns = proc->stime + proc->utime; - if (total_existing_time && 1E-6 < time_interval) { - uint64_t total_time_diff = total_current_time - total_existing_time; - proc->super.percent_cpu = ((double)total_time_diff / time_interval) * 100.0; + uint64_t user_time_ns = machTicksToNanoseconds(pti.pti_total_user); + uint64_t system_time_ns = machTicksToNanoseconds(pti.pti_total_system); + + uint64_t total_current_time_ns = user_time_ns + system_time_ns; + + if (total_existing_time_ns && 1E-6 < time_interval_ns) { + uint64_t total_time_diff_ns = total_current_time_ns - total_existing_time_ns; + proc->super.percent_cpu = ((double)total_time_diff_ns / time_interval_ns) * 100.0; } else { proc->super.percent_cpu = 0.0; } - proc->super.time = total_current_time / 10000000; + proc->super.time = nanosecondsToCentiseconds(total_current_time_ns); proc->super.nlwp = pti.pti_threadnum; proc->super.m_virt = pti.pti_virtual_size / ONE_K; proc->super.m_resident = pti.pti_resident_size / ONE_K; @@ -352,8 +370,8 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 / (double)dpl->host_info.max_mem; - proc->stime = pti.pti_total_system; - proc->utime = pti.pti_total_user; + 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;*/ diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h index f01efce1..9c56639b 100644 --- a/darwin/DarwinProcess.h +++ b/darwin/DarwinProcess.h @@ -31,7 +31,7 @@ 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 time_interval); +void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval_ns); /* * Scan threads for process state information. diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 60f8a7ca..3cfdca6f 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -160,9 +160,11 @@ void ProcessList_delete(ProcessList* this) { free(this); } -static double ticksToNanoseconds(const double ticks) { +// Converts "scheduler ticks" to nanoseconds. +// See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_clockTicksPerSec` constant. +static double schedulerTicksToNanoseconds(const double ticks) { const double nanos_per_sec = 1e9; - return (ticks / Platform_timebaseToNS) * (nanos_per_sec / (double) Platform_clockTicksPerSec); + return ticks * (nanos_per_sec / (double) Platform_clockTicksPerSec); } void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { @@ -192,7 +194,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } } - const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.activeCPUs; + const double time_interval_ns = schedulerTicksToNanoseconds(dpl->global_diff) / (double) dpl->super.activeCPUs; /* Clear the thread counts */ super->kernelThreads = 0; @@ -213,7 +215,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { 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); + 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;