Skip to content

Commit a01af46

Browse files
committed
Bring back conversion of process CPU time on macOS (#1638)
1 parent e1db96a commit a01af46

File tree

6 files changed

+72
-2
lines changed

6 files changed

+72
-2
lines changed

configure.ac

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

398398
if test "$my_htop_platform" = darwin; then
399+
AC_CHECK_FUNCS([mach_timebase_info])
400+
399401
AC_CHECK_FUNCS([host_statistics64], [
400402
AC_CHECK_TYPES([struct vm_statistics64], [], [], [[#include <mach/vm_statistics.h>]])
401403
], [])

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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,15 @@ const MeterClass* const Platform_meterTypes[] = {
151151
NULL
152152
};
153153

154+
static uint64_t Platform_nanosecondsPerMachTickNumer = 1;
155+
static uint64_t Platform_nanosecondsPerMachTickDenom = 1;
156+
154157
static double Platform_nanosecondsPerSchedulerTick = -1;
155158

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

158161
bool Platform_init(void) {
162+
Platform_calculateNanosecondsPerMachTick(&Platform_nanosecondsPerMachTickNumer, &Platform_nanosecondsPerMachTickDenom);
159163

160164
// Determine the number of scheduler clock ticks per second
161165
errno = 0;
@@ -171,6 +175,21 @@ bool Platform_init(void) {
171175
return true;
172176
}
173177

178+
// Converts ticks in the Mach "timebase" to nanoseconds.
179+
// See `mach_timebase_info`, as used to define the `Platform_nanosecondsPerMachTick` constant.
180+
uint64_t Platform_machTicksToNanoseconds(uint64_t mach_ticks) {
181+
uint64_t ticks_quot = mach_ticks / Platform_nanosecondsPerMachTickDenom;
182+
uint64_t ticks_rem = mach_ticks % Platform_nanosecondsPerMachTickDenom;
183+
184+
uint64_t part1 = ticks_quot * Platform_nanosecondsPerMachTickNumer;
185+
186+
// When Platform_nanosecondsPerMachTickDenom * Platform_nanosecondsPerMachTickNumer is less than 2^64, ticks_rem *
187+
// Platform_nanosecondsPerMachTickNumer will be less than 2^64 as well, i.e. never overflows.
188+
uint64_t part2 = (ticks_rem * Platform_nanosecondsPerMachTickNumer) / Platform_nanosecondsPerMachTickDenom;
189+
190+
return part1 + part2;
191+
}
192+
174193
// Converts "scheduler ticks" to nanoseconds.
175194
// See `sysconf(_SC_CLK_TCK)`, as used to define the `Platform_nanosecondsPerSchedulerTick` constant.
176195
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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,45 @@ 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+
93+
#ifdef __x86_64__
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+
* Rosetta 2 only supports x86-64, so skip this workaround when building for other architectures.
102+
*/
103+
104+
bool isRunningUnderRosetta2 = Platform_isRunningTranslated();
105+
106+
// Kernel versions >= 20.0.0 (macOS 11.0 AKA Big Sur) affected
107+
bool isBuggedVersion = 0 <= Platform_CompareKernelVersion((KernelVersion) {20, 0, 0});
108+
109+
if (isRunningUnderRosetta2 && isBuggedVersion) {
110+
// In this case `mach_timebase_info` provides the wrong value, so we hard-code the correct factor,
111+
// as determined from `mach_timebase_info` as if the process was running natively.
112+
*numer = 125;
113+
*denom = 3;
114+
return;
115+
}
116+
#endif
117+
118+
#ifdef HAVE_MACH_TIMEBASE_INFO
119+
mach_timebase_info_data_t info = { 0 };
120+
if (mach_timebase_info(&info) == KERN_SUCCESS) {
121+
*numer = info.numer;
122+
*denom = info.denom;
123+
return;
124+
}
125+
#endif
126+
127+
// No info on actual timebase found; assume timebase in nanoseconds.
128+
*numer = 1;
129+
*denom = 1;
130+
}

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)