Last Week on My Mac: Times forgotten

Always read the small print. Carefully. Even if it doesn’t seem to apply to you, study it until you’re certain it doesn’t before putting it aside.

Some weeks ago, I was caught out by this: I’d been looking carefully through Apple’s detailed article on architectural differences between Intel and Apple Silicon Macs. As all my apps are written in Swift, and attempt fairly mundane high-level things, I skipped quickly down through warnings about changes in the Video Toolbox, and about using raw assembly code, vector instructions, and Mach Absolute Time values. Nothing applied to my apps, so I moved on to wrestling with issues such as version numbering, which seemed more pressing.

It was the section on Mach Absolute Time which I should have pondered more deeply. The Mach clock in all Intel Macs ticks once every nanosecond, and the absolute time values it returns are thus in nanoseconds. That changes with Apple Silicon Macs, which could tick at other frequencies, requiring their absolute time values to be scaled using a conversion factor to deliver them in nanoseconds.

What I didn’t spot then was that one of my apps, RouteMap, does indirectly use Mach Absolute Time values which need to be corrected when running on Apple Silicon. Not that RouteMap obtains any time value from mach_absolute_time, which Apple warns about, but the app calculates using values which are derived from that, it appears. This is an issue going right back to the introduction of the unified log in Sierra.

Apple introduced the unified log four years ago. Although developers were given a brief outline of how to write to it in a presentation at WWDC 2016, Apple has singularly failed to produce any coherent documentation beyond scrappy asides which lie buried in places like the man page for the log command. Nowhere among those does Apple define or explain the data fields contained within log entries. In particular, that apparently known as machTimestamp isn’t mentioned, nor is its origin revealed.

It isn’t, of course, hard to guess that the machTimestamp field in the unified log contains the instantaneous value of mach_absolute_time at the moment that entry is written, but without that documentation Apple’s own engineers and technical authors could only have become aware of that through implication and experience. Anyone writing an account of the architectural differences between Intel and Apple Silicon Macs and their importance to developers and users would therefore readily overlook effects on entries in the unified log, specifically in the machTimestamp field.

As an undocumented field in log entries, why should anyone want to use the value of machTimestamp, though?

One of the great strengths of the unified log is the precision of its data. The normal timestamp field resolves to microseconds, each being a thousand ticks of the machTimestamp in an Intel Mac. In many cases, particularly when you’re parsing log entries from text, or want to calculate differences in time, it’s easier to use integer values in machTimestamp rather than parse the more complex and less precise value in the full timestamp field. That’s why RouteMap uses machTimestamp values.

Apple’s formerly coherent documentation of Mach time in Technical Q&A QA1398 never progressed beyond being a Q&A, has long since been archived, and hasn’t been touched for fifteen years anyway. As it was written more than a decade before the introduction of the unified log, it couldn’t have made any association between mach_absolute_time and the machTimestamp field. So unless the reader had already made that connection, it would pass unnoticed.

Mac OS X used to provide convenience calls in its CoreServices framework to convert Mach ticks to nanoseconds, which made it simple to handle Mach ticks on PowerPC systems. They were dropped some years ago, and although it’s hardly tricky to perform the conversion longhand, with every Mac model ticking away in nanoseconds on Intel processors, it has been so easy just to assume that’s going to remain the case and skip the conversion.

With Apple’s primary documentation about working with Mach Absolute Time long forgotten, its conversion functions stripped from the macOS SDK, and missing documentation for the unified log, it’s not surprising that whoever wrote that otherwise commendable section in its latest documentation failed to mention any effect on interpreting log entries.

Apple has tried to compensate for translated Intel processes which fail to allow for Apple Silicon Macs running Mach Absolute Time at a different frequency: it advises that “the value returned by the mach_absolute_time function is different for native and translated processes, and doesn’t necessarily correspond to the number of nanoseconds since boot”.

Unfortunately, that doesn’t apply to the values stored in the log’s machTimestamp field, which must be converted to nanoseconds using the conversion for native rather than translated processes. This means that, had I been more prescient and converted values obtained by Intel-only versions of my app RouteMap, when translated by Rosetta 2 to run on Apple Silicon, they would have used the wrong conversion factor. Apple’s remedy introduces further problems for any app which has to work with stored values of Mach Absolute Time, and bugs which will be fiendishly difficult to locate or fix.

The effects of neglecting to document macOS properly are both insidious and pervasive. In the case of Mach Absolute Time, missing documentation has prevented Apple from noticing consequences which could significantly affect users of Apple Silicon Macs. Good documentation is wise investment which invariably pays off, and spares everyone from unseen implications of the small print.