Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Darwin running time #1622

Merged
merged 6 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,6 @@ AC_CHECK_FUNCS([ \
])

if test "$my_htop_platform" = darwin; then
AC_CHECK_FUNCS([mach_timebase_info])

AC_CHECK_FUNCS([host_statistics64], [
AC_CHECK_TYPES([struct vm_statistics64], [], [], [[#include <mach/vm_statistics.h>]])
], [])
Expand Down
80 changes: 31 additions & 49 deletions darwin/DarwinProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,6 @@ static void DarwinProcess_updateCmdLine(const struct kinfo_proc* k, Process* pro
Process_updateCmdline(proc, k->kp_proc.p_comm, 0, strlen(k->kp_proc.p_comm));
}

// Converts nanoseconds to hundredths of a second (centiseconds) as needed by the "time" field of the Process struct.
static long long int nanosecondsToCentiseconds(uint64_t nanoseconds) {
const uint64_t centiseconds_per_second = 100;
const uint64_t nanoseconds_per_second = 1e9;
return nanoseconds / nanoseconds_per_second * centiseconds_per_second;
}

static char* DarwinProcess_getDevname(dev_t dev) {
if (dev == NODEV) {
return NULL;
Expand Down Expand Up @@ -369,40 +362,41 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable* dpt, double timeIntervalNS) {
struct proc_taskinfo 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;
if (PROC_PIDTASKINFO_SIZE != proc_pidinfo(Process_getPid(&proc->super), PROC_PIDTASKINFO, 0, &pti, PROC_PIDTASKINFO_SIZE)) {
proc->taskAccess = false;
return;
}

uint64_t user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user);
uint64_t system_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_system);
const DarwinMachine* dhost = (const DarwinMachine*) proc->super.super.host;

uint64_t total_current_time_ns = user_time_ns + system_time_ns;
uint64_t total_existing_time_ns = proc->stime + proc->utime;
uint64_t user_time_ns = pti.pti_total_user;
uint64_t system_time_ns = pti.pti_total_system;
uint64_t total_current_time_ns = user_time_ns + system_time_ns;

if (total_existing_time_ns && 1E-6 < timeIntervalNS) {
uint64_t total_time_diff_ns = total_current_time_ns - total_existing_time_ns;
proc->super.percent_cpu = ((double)total_time_diff_ns / timeIntervalNS) * 100.0;
} else {
proc->super.percent_cpu = 0.0;
}
Process_updateCPUFieldWidths(proc->super.percent_cpu);

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;
proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dhost->host_info.max_mem;

proc->stime = system_time_ns;
proc->utime = user_time_ns;

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;
if (total_existing_time_ns < total_current_time_ns) {
const uint64_t total_time_diff_ns = total_current_time_ns - total_existing_time_ns;
proc->super.percent_cpu = ((double)total_time_diff_ns / timeIntervalNS) * 100.0;
} else {
proc->super.percent_cpu = 0.0;
}
Process_updateCPUFieldWidths(proc->super.percent_cpu);

proc->super.state = pti.pti_numrunning > 0 ? RUNNING : SLEEPING;
proc->super.time = total_current_time_ns / 1e7;
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;
proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 / (double)dhost->host_info.max_mem;

proc->stime = system_time_ns;
proc->utime = user_time_ns;

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;
}

/*
Expand Down Expand Up @@ -434,18 +428,6 @@ void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt) {
return;
}

{
task_info_data_t tinfo;
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
ret = task_info(task, TASK_BASIC_INFO, (task_info_t) &tinfo, &task_info_count);
if (ret != KERN_SUCCESS) {
CRT_debug("task_info(%d) failed: %s", pid, mach_error_string(ret));
dp->taskAccess = false;
mach_port_deallocate(mach_task_self(), task);
return;
}
}

thread_array_t thread_list;
mach_msg_type_number_t thread_count;
ret = task_threads(task, &thread_list, &thread_count);
Expand Down
25 changes: 7 additions & 18 deletions darwin/DarwinProcessTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,6 @@ in the source distribution for its full text.
#include "zfs/ZfsArcStats.h"


static ProcessState ProcessTable_mapDarwinProcessState(int kinfo_proc_state) {
switch(kinfo_proc_state) {
case SRUN:
return RUNNING;
case SSLEEP:
return SLEEPING;
case SSTOP:
return STOPPED;
case SZOMB:
return ZOMBIE;
case SIDL:
return IDLE;
default:
return UNKNOWN;
}
}

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;
Expand Down Expand Up @@ -120,11 +103,17 @@ void ProcessTable_goThroughEntries(ProcessTable* super) {
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpt, time_interval_ns);

// Deduce further process states not covered in the libproc call above
if (ps[i].kp_proc.p_stat == SZOMB) {
proc->super.state = ZOMBIE;
} else if (ps[i].kp_proc.p_stat == SSTOP) {
proc->super.state = STOPPED;
}

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);
}
proc->super.state = ProcessTable_mapDarwinProcessState(ps[i].kp_proc.p_stat);

// Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0});
Expand Down
9 changes: 0 additions & 9 deletions darwin/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,11 @@ const MeterClass* const Platform_meterTypes[] = {
NULL
};

static double Platform_nanosecondsPerMachTick = 1.0;

static double Platform_nanosecondsPerSchedulerTick = -1;

static mach_port_t iokit_port; // the mach port used to initiate communication with IOKit

bool Platform_init(void) {
Platform_nanosecondsPerMachTick = Platform_calculateNanosecondsPerMachTick();

// Determine the number of scheduler clock ticks per second
errno = 0;
Expand All @@ -171,12 +168,6 @@ bool Platform_init(void) {
return true;
}

// Converts ticks in the Mach "timebase" to nanoseconds.
// See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
uint64_t Platform_machTicksToNanoseconds(uint64_t mach_ticks) {
return (uint64_t) ((double) mach_ticks * Platform_nanosecondsPerMachTick);
}

// Converts "scheduler ticks" to nanoseconds.
// See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_nanosecondsPerSchedulerTick` constant.
double Platform_schedulerTicksToNanoseconds(const double scheduler_ticks) {
Expand Down
4 changes: 0 additions & 4 deletions darwin/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ extern const MeterClass* const Platform_meterTypes[];

bool Platform_init(void);

// Converts ticks in the Mach "timebase" to nanoseconds.
// See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
uint64_t Platform_machTicksToNanoseconds(uint64_t mach_ticks);

// Converts "scheduler ticks" to nanoseconds.
// See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_nanosecondsPerSchedulerTick` constant.
double Platform_schedulerTicksToNanoseconds(const double scheduler_ticks);
Expand Down
34 changes: 0 additions & 34 deletions darwin/PlatformHelpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,37 +86,3 @@ bool Platform_isRunningTranslated(void) {
}
return ret;
}

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
return 1.0;
#else
mach_timebase_info_data_t info;

/* WORKAROUND for `mach_timebase_info` giving incorrect values on M1 under Rosetta 2.
* rdar://FB9546856 https://openradar.appspot.com/radar?id=5055988478509056
*
* We don't know exactly what feature/attribute of the M1 chip causes this mistake under Rosetta 2.
* Until we have more Apple ARM chips to compare against, the best we can do is special-case
* the "Apple M1" chip specifically when running under Rosetta 2.
*/

bool isRunningUnderRosetta2 = Platform_isRunningTranslated();

// Kernel version 20.0.0 is macOS 11.0 (Big Sur)
bool isBuggedVersion = Platform_KernelVersionIsBetween((KernelVersion) {20, 0, 0}, (KernelVersion) {999, 999, 999});

if (isRunningUnderRosetta2 && isBuggedVersion) {
// In this case `mach_timebase_info` provides the wrong value, so we hard-code the correct factor,
// as determined from `mach_timebase_info` when the process running natively.
info = (mach_timebase_info_data_t) { .numer = 125, .denom = 3 };
} else {
// No workarounds needed, use the OS-provided value.
mach_timebase_info(&info);
}

return (double)info.numer / (double)info.denom;
#endif
}
4 changes: 0 additions & 4 deletions darwin/PlatformHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ int Platform_CompareKernelVersion(KernelVersion v);
// lowerBound <= currentVersion < upperBound
bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upperBound);

double Platform_calculateNanosecondsPerMachTick(void);

void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize);

bool Platform_isRunningTranslated(void);

double Platform_calculateNanosecondsPerMachTick(void);

#endif
Loading