Skip to content

Commit 884bff9

Browse files
committed
Bring back conversion of process CPU time on macOS (#1638)
1 parent 7720dbd commit 884bff9

File tree

6 files changed

+60
-2
lines changed

6 files changed

+60
-2
lines changed

configure.ac

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ return ptr != &ch; /* Should be 0 (exit success) */
413413
CFLAGS=$htop_save_CFLAGS
414414

415415
if test "$my_htop_platform" = darwin; then
416+
AC_CHECK_FUNCS([mach_timebase_info])
417+
416418
AC_CHECK_FUNCS([host_statistics64], [
417419
AC_CHECK_TYPES([struct vm_statistics64], [], [], [[#include <mach/vm_statistics.h>]])
418420
], [])

darwin/DarwinProcess.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,8 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable
372372
const DarwinMachine* dhost = (const DarwinMachine*) proc->super.super.host;
373373

374374
uint64_t total_existing_time_ns = proc->stime + proc->utime;
375-
uint64_t user_time_ns = pti.pti_total_user;
376-
uint64_t system_time_ns = pti.pti_total_system;
375+
uint64_t user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user);
376+
uint64_t system_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_system);
377377
uint64_t total_current_time_ns = user_time_ns + system_time_ns;
378378

379379
if (total_existing_time_ns < total_current_time_ns) {

darwin/Platform.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,15 @@ const MeterClass* const Platform_meterTypes[] = {
148148
NULL
149149
};
150150

151+
static uint64_t Platform_nanosecondsPerMachTickNumer = 1;
152+
static uint64_t Platform_nanosecondsPerMachTickDenom = 1;
153+
151154
static double Platform_nanosecondsPerSchedulerTick = -1;
152155

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

155158
bool Platform_init(void) {
159+
Platform_calculateNanosecondsPerMachTick(&Platform_nanosecondsPerMachTickNumer, &Platform_nanosecondsPerMachTickDenom);
156160

157161
// Determine the number of scheduler clock ticks per second
158162
errno = 0;
@@ -168,6 +172,18 @@ bool Platform_init(void) {
168172
return true;
169173
}
170174

175+
// Converts ticks in the Mach "timebase" to nanoseconds.
176+
// See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
177+
uint64_t Platform_machTicksToNanoseconds(uint64_t mach_ticks) {
178+
uint64_t ticks_quot = mach_ticks / Platform_nanosecondsPerMachTickDenom;
179+
uint64_t ticks_rem = mach_ticks % Platform_nanosecondsPerMachTickDenom;
180+
181+
uint64_t part1 = ticks_quot * Platform_nanosecondsPerMachTickNumer;
182+
uint64_t part2 = (ticks_rem * Platform_nanosecondsPerMachTickNumer) / Platform_nanosecondsPerMachTickDenom;
183+
184+
return part1 + part2;
185+
}
186+
171187
// Converts "scheduler ticks" to nanoseconds.
172188
// See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_nanosecondsPerSchedulerTick` constant.
173189
double Platform_schedulerTicksToNanoseconds(const double scheduler_ticks) {

darwin/Platform.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ extern const MeterClass* const Platform_meterTypes[];
3838

3939
bool Platform_init(void);
4040

41+
// Converts ticks in the Mach "timebase" to nanoseconds.
42+
// See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick*` constants.
43+
uint64_t Platform_machTicksToNanoseconds(uint64_t mach_ticks);
44+
4145
// Converts "scheduler ticks" to nanoseconds.
4246
// See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_nanosecondsPerSchedulerTick` constant.
4347
double Platform_schedulerTicksToNanoseconds(const double scheduler_ticks);

darwin/PlatformHelpers.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,36 @@ bool Platform_isRunningTranslated(void) {
8686
}
8787
return ret;
8888
}
89+
90+
void Platform_calculateNanosecondsPerMachTick(uint64_t* numer, uint64_t* denom) {
91+
// Check if we can determine the timebase used on this system.
92+
// If the API is unavailable assume we get our timebase in nanoseconds.
93+
#ifdef HAVE_MACH_TIMEBASE_INFO
94+
/* WORKAROUND for `mach_timebase_info` giving incorrect values on M1 under Rosetta 2.
95+
* rdar://FB9546856 http://www.openradar.appspot.com/FB9546856
96+
*
97+
* We don't know exactly what feature/attribute of the M1 chip causes this mistake under Rosetta 2.
98+
* Until we have more Apple ARM chips to compare against, the best we can do is special-case
99+
* the "Apple M1" chip specifically when running under Rosetta 2.
100+
*/
101+
102+
bool isRunningUnderRosetta2 = Platform_isRunningTranslated();
103+
104+
// Kernel version 20.0.0 is macOS 11.0 (Big Sur)
105+
bool isBuggedVersion = 0 <= Platform_CompareKernelVersion((KernelVersion) {20, 0, 0});
106+
107+
if (isRunningUnderRosetta2 && isBuggedVersion) {
108+
// In this case `mach_timebase_info` provides the wrong value, so we hard-code the correct factor,
109+
// as determined from `mach_timebase_info` when the process running natively.
110+
*numer = 125;
111+
*denom = 3;
112+
} else {
113+
// No workarounds needed, use the OS-provided value.
114+
mach_timebase_info_data_t info;
115+
mach_timebase_info(&info);
116+
117+
*numer = info.numer;
118+
*denom = info.denom;
119+
}
120+
#endif
121+
}

darwin/PlatformHelpers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ in the source distribution for its full text.
88
*/
99

1010
#include <stdbool.h>
11+
#include <stdint.h>
1112
#include <sys/types.h>
1213

1314

@@ -29,6 +30,8 @@ int Platform_CompareKernelVersion(KernelVersion v);
2930
// lowerBound <= currentVersion < upperBound
3031
bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upperBound);
3132

33+
void Platform_calculateNanosecondsPerMachTick(uint64_t* numer, uint64_t* denom);
34+
3235
void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize);
3336

3437
bool Platform_isRunningTranslated(void);

0 commit comments

Comments
 (0)