Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Last Week on My Mac: Checking code can take longer now

By: hoakley
4 May 2025 at 15:00

Many of you have commented that you too find that apps and command tools can now take surprisingly long to launch. Although my previous analyses have demonstrated how those can often be attributed to security checks being made on components including frameworks and dylibs, there remains some dispute over the nature of those checks. Although I believe there’s convincing evidence that those checks are prolonged to recompute hashes and CDHashes, others are adamant that they are in fact ‘malware scans’. This article considers new evidence.

Inspecting and analysing the log during the launch of large apps isn’t the best way of getting a clear view of what happens when macOS runs its security checks. There’s a great deal going on at the time, with multiple security checks being performed for TCC, LaunchServices and RunningBoard in dialog, sandboxes and containers to set up, and iCloud services to connect. Last week I’ve been tackling this more methodically, and here use two launches of a simple command tool to give clearer insights into what’s going on when macOS runs its security checks.

Methods

The command tool in question is my blowhole, just over 200 KB of simple code to write a message to the log, hence directly telling us when it’s run. It has its signing certificate embedded into its Mach-O binary, and is notarized. However, because it’s a single binary and not an app bundle, its notarization ticket is stapled to its installer package, and not to the tool itself.

The two runs I analyse here are:

  • On a Mac Studio M1 Max running macOS Ventura 13.4.1 at 14:32 on 4 July 2023. blowhole had already been installed on that Mac, but the copy being run was in a previously unknown location, ~/Documents, which normally forces macOS to perform more extensive security checks, including an XProtect scan and notarization checks, but not as thorough as when in quarantine.
  • On a Mac mini M4 Pro running macOS Sequoia 15.4.1 at 20:07 on 2 May 2025. Although blowhole had been installed and run some weeks previously, it hadn’t been run since then, and was expected to attract more extensive security checks, including an XProtect scan and notarization checks, but not as thorough as when in quarantine.

The first of those was collected using Ulbow, and the second by LogUI. Excerpts giving milestone entries are given in the Appendix at the end. In the diagrams below, each milestone is given with time in seconds elapsed since the first mention of the binary.

Ventura

First mention of the tool to be run comes from AppleMobileFileIntegrity (AMFI), which leads immediately to its daemon amfid starting its assessment of the binary, with the entry SecTrustEvaluateIfNecessary to inspect the signature and the CDHashes it contains. As this takes a mere 0.003 seconds, and the next stage starts 0.01 seconds after amfid entered the path to the binary, all that could have done was confirm the integrity of the signature, its requirements, and that its hashes were already cached.

syspolicyd then records the start of the Gatekeeper process assessment, and initiates the Gatekeeper scan, first starting checks on the notarization ticket, then starting the XProtect scan while ticket checking proceeds. Of those, the XProtect scan completes first, returning its results to syspolicyd at 0.054 seconds after the start.

Ticket checking involves an explicit connection to iCloud using CloudKit, with abundant log entries. The CloudKit Ticket Store is found to be reachable, and the ticket checked for the CDHashes obtained earlier from the tool’s signature.

With both checks completed satisfactorily, at 0.192 seconds the Gatekeeper scan is declared complete, syspolicyd evaluates its result, and is almost ready for the tool to run. Before that can happen, details of the executable are entered into provenance tracking. syspolicyd confirms the evaluation allows the tool to run, and AppleSystemPolicy records the evaluation result.

Sequoia

The sequence here is very similar to that in Ventura, with some significant differences, marked in the emphasised items in the diagram above.

First, there’s a substantial delay of 0.063 seconds between amfid entering the binary’s path and the start of the Gatekeeper process assessment. This started with entries from amfid recording SecTrustEvaluateIfNecessary and trustd SecKeyVerifySignature, indicating that more took place here than in Ventura. However, there’s no evidence of any external signature checks being made, and it’s most likely that the binary’s hashes weren’t cached, so required recomputation to verify them. The delay is woefully inadequate for any form of malware scan to have taken place at this stage.

When XprotectService reports that XProtect is performing the malware scan, it additionally reports the location of the XProtect rules being used. That’s because Sequoia introduced a new location used for those data files, in /var/protected/xprotect/XProtect.bundle/Contents/Resources/XProtect.yara as recorded here.

Next, the XProtect scan here takes 0.126 seconds, rather than 0.030 seconds in Ventura. This is the result of the huge growth in the number and complexity of the Yara rules used for this scan over the last two years. The Ventura scan was performed using version 2168 of those rules, with a Yara file of 147 KB size and around 218 rules. By version 5296 used in the Sequoia scan, file size had risen to 947 KB with about 381 rules.

Size of the binary being scanned also affects scan time, although in an unexpected way. A great many of the Yara rules used include upper limits to file size, so those larger than a few MB are subject to few rules, probably intentionally. Thus, larger binary files are likely to complete their XProtect scan in shorter time than expected, and maybe more quickly than smaller binaries.

As a result of these two delays, Gatekeeper’s XProtect results aren’t reported until 0.247 seconds have elapsed since the start, already over 0.05 seconds longer than the whole process in Ventura. However, in this case there’s no mention of provenance tracking, and the blowhole tool is finally run after 0.3 seconds, taking just over 150% of the time in Ventura.

Summary of security checks

  • Trust evaluation and signature verification, to confirm hashes and CDHashes if not cached;
  • Gatekeeper scan, including simultaneous ticket check and XProtect malware scan;
  • CDHash ticket check online using CloudKit with iCloud Ticket Store;
  • XProtect malware scan against Yara rules;
  • Gatekeeper evaluation for syspolicyd to allow or not;
  • Result registered with the kernel;
  • Command tool run.

Note that there’s no evidence of any OCSP checks being made with the certificate authority to determine whether certificates have been revoked. Additional time will be required if hashes and CDHashes are to be recomputed, and as a result of increased Yara rules.

Appendix: Log Milestones

Times are given in seconds, adjusted to a start of 0.0. The Ventura extract was obtained with log privacy disabled.

Ventura 13.4.1 2023-07-04 14:32:40 on Mac Studio M1 Max

XProtect version 2168, Yara file 147 KB, 218 rules
0.000000 AppleMobileFileIntegrity Checking in with amfid for DER co.eclecticlight.blowhole
0.001395 amfid Entering OSX path for /Users/howardoakley/Documents/blowhole
0.010608 syspolicyd GK process assessment: /Users/howardoakley/Documents/blowhole <-- (/bin/zsh, /System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal)
0.022891 syspolicyd GK performScan: PST: (path: /Users/howardoakley/Documents/blowhole), (team: (null)), (id: (null)), (bundle_id: (null))
0.023291 syspolicyd looking up ticket: {length = 20, bytes = 0xe0cad936293cea0807ec2e5193bed5f3f02dc019}, 2, 1
0.023316 syspolicyd cloudkit record fetch: https://api.apple-cloudkit.com/database/1/com.apple.gk.ticket-delivery/production/public/records/lookup, 2/2/e0cad936293cea0807ec2e5193bed5f3f02dc019
0.023554 XprotectService Xprotect is performing a direct malware and dylib scan: /Users/howardoakley/Documents/blowhole
0.053971 syspolicyd GK Xprotect results: PST: (path: /Users/howardoakley/Documents/blowhole), (team: (null)), (id: (null)), (bundle_id: (null)), {
XProtectMalwareType = 0;
XProtectSignatureVersion = 4321574501753746074;
}, version: 4321574501753746074
0.170283 syspolicyd CKTicketStore network reachability: 1, Wed Jun 21 19:33:35 2023
0.191576 syspolicyd GK scan complete: PST: (path: /Users/howardoakley/Documents/blowhole), (team: (null)), (id: (null)), (bundle_id: (null)), 4, 4, 0
0.191797 syspolicyd GK evaluateScanResult: 2, PST: (path: /Users/howardoakley/Documents/blowhole), (team: QWY4LRW926), (id: co.eclecticlight.blowhole), (bundle_id: NOT_A_BUNDLE), 0, 0, 1, 0, 4, 4, 0
0.191996 syspolicyd Putting executable into provenance with metadata: TA(917fa0aed8a1a838, 0)
0.191999 syspolicyd Putting process into provenance tracking with metadata: 1410, TA(917fa0aed8a1a838, 0)
0.192054 syspolicyd GK eval - was allowed: 1, show prompt: 0
0.192090 AppleSystemPolicy evaluation result: 17, allowed, cache, 1688477560
0.195467 blowhole Blowhole snorted!

Sequoia 15.4.1 2025-05-02 20:07:00 on Mac mini M4 Pro

XProtect version 5296, Yara file 947 KB, 381 rules
0.000 amfid Entering OSX path for /usr/local/bin/blowhole
0.063 syspolicyd GK process assessment: <private> <-- (<private>, <private>)
0.081 syspolicyd GK performScan: PST: (path: cc151acaee5bc8cd), (team: (null)), (id: (null)), (bundle_id: (null))
0.082 syspolicyd looking up ticket: <private>, 2, 1
0.082 syspolicyd cloudkit record fetch: <private>, <private>
0.121 XprotectService Xprotect is performing a direct malware and dylib scan: <private>
0.125 XprotectService Using XProtect rules location: /var/protected/xprotect/XProtect.bundle/Contents/Resources/XProtect.yara
0.247 syspolicyd GK Xprotect results: PST: (path: cc151acaee5bc8cd), (team: (null)), (id: (null)), (bundle_id: (null)), XPScan: 0,1089725382763820427,2025-05-02 19:07:00 +0000,(null),(null),file:///usr/local/bin/blowhole
0.283 syspolicyd CKTicketStore network reachability: 1, Fri May 2 17:21:52 2025
0.293 syspolicyd GK scan complete: PST: (path: cc151acaee5bc8cd), (team: (null)), (id: (null)), (bundle_id: (null)), 4, 4, 0
0.295 syspolicyd GK evaluateScanResult: 2, PST: (path: cc151acaee5bc8cd), (team: QWY4LRW926), (id: co.eclecticlight.blowhole), (bundle_id: NOT_A_BUNDLE), 0, 0, 1, 0, 4, 4, 0
0.296 syspolicyd GK eval - was allowed: 1, show prompt: 0
0.296 kernel evaluation result: 5, exec, allowed, cache, 1746212820, 4, 4, f1f7b76465d358b, 1746212820, /usr/local/bin/blowhole
0.303 blowhole Blowhole snorted!

For comparison, Catalina’s log entries are remarkably similar too.

Why some apps sometimes launch extremely slowly

By: hoakley
30 April 2025 at 14:30

So far, I have looked at slow launching in two apps, Pages and Calibre. Many of the complaints about the slowest to launch have been levelled at apps in Affinity’s popular suite. Thanks to the efforts of Alain, who generously obtained and provided me with log extracts, I can now try to explain why some apps may launch extremely slowly, occasionally taking more than 30 seconds, and on some Macs several minutes.

Previous work

In the first of my investigations, I demonstrated how Pages and Calibre could take a long time checking frameworks in their app bundle, and how that appeared to account for modest delays. I then looked more closely at Calibre, and found an association between slow launching and cache misses when checking frameworks. One possible explanation for that is the recomputation of SHA-256 hashes used to generate the CDHashes that both identify and verify protected code, and in my third article I showed how that could account for large differences seen in older models.

Affinity Designer 2

Alain has provided me with two particularly valuable log extracts from his MacBook Pro M1 Pro running macOS Monterey. Both were collected during launches of Affinity Designer 2, one from a very slow launch taking close to 30 seconds, and the other from a normal, brisk launch taking little more than a second.

Differences between those are stark: the slow launch took 23.5 seconds from mouse-click to loading user preferences for the app, of which 22.3 seconds was accounted for by framework checks, all of which were cache misses. The fast launch took a total of only 1.0 second and didn’t record any framework or other security checks at all, as it was performed about 12 minutes after the slow launch took place.

Framework checks

Framework checks performed during the slow launch consisted of a similar sequence of log entries to those seen in Pages and Calibre:
10.943986 AppleSystemPolicy Waking up reference: 173
10.944007 AppleSystemPolicy Thread waiting on reference 173 woke up
10.944014 AppleSystemPolicy evaluation result: 173, allowed, cache, 1745876410
10.952412 AppleMobileFileIntegrity AMFI: constraint violation /Applications/Affinity Designer 2.app/Contents/Frameworks/liblibpersona.dylib has entitlements but is not a main binary
10.961105 amfid Entering OSX path for /Applications/Affinity Designer 2.app/Contents/Frameworks/liblibpersona.dylib
10.982192 Security SecTrustEvaluateIfNecessary
10.987489 Security SecTrustEvaluateIfNecessary
11.007538 Security SecTrustEvaluateIfNecessary
11.011106 Security SecTrustEvaluateIfNecessary
11.012004 com.apple.syspolicy.exec Recording cache miss for <private>
20.898736 AppleSystemPolicy Waking up reference: 174

Entries here differ slightly from excerpts for Pages and Calibre, as these are from Monterey rather than Sonoma.

In that case, the time interval between ‘waking up’ the two references is the checking cycle time for liblibpersona.dylib in the app’s Frameworks folder, an amazing 9.955 seconds. That dylib is 1.08 GB in size, and the largest of all the components in the app’s frameworks. If that period was accounted for by computing the dylib’s SHA-256 hash, that would represent a rate of 108 MB/s.

Checking cycle times varied greatly, ranging from 0.03-9.96 seconds over a total of 61 frameworks. As the total size of the Frameworks folder is 2.3 GB, the overall rate is 103 MB/s, similar to that for liblibpersona.dylib alone.

What’s being checked?

Log entries don’t provide any information as to what SecTrustEvaluateIfNecessary is likely to perform on these frameworks that can take anything from 0.03-9.96 seconds for each framework. Many of them are far too short to involve any online check, and in any case those are distinctive in their log entries. Malware scan using any known Yara rules is most unlikely, as:

  • XProtect Yara rules commonly include file size limits, resulting in few rules applying to larger files, and more rapid completion.
  • Known checks using Yara rules are all well-recorded in log entries, and the source of those rules is stated clearly.
  • Yara scans are normally reported with their result.
  • Scan results are succinct and hardly likely to be lost in a ‘cache miss’.

The most likely activity to account for these long checking times is computation of SHA-256 hashes for the contents of each item in the app’s Frameworks folder. Thus, these occasional extremely long launch times are most probably due to time taken ‘evaluating trust’ by computing hashes for protected code in the Frameworks folder in the app bundle, when those hashes have been flushed from their cache.

Why so extremely slow?

Several events have been credited with causing very long launch times, among them starting the Mac up, and installing updates to XProtect data. It appears most likely that the latter might act indirectly, by flushing at least some of the caches, which may be intentional.

Workarounds

The only strategy that does appear to stop long launch times in most cases is to disable SIP and, in the case of Apple silicon Macs, to set the security mode to Permissive. As I have noted on several occasions, that results in major changes in app security as well. It also disables some features that may be required, including Wallet and Apple Pay. It’s worth being aware that, once so disabled, if those features are to be restored they have to be created from scratch again, and can’t simply be toggled back on. Other side-effects of Permissive Security mode include the refusal of some App Store apps to run.

Those who find the occasional extremely slow app launch unacceptable, but wish to continue to run in Full Security, can pre-warm apps likely to be affected at the start of each session, so making it unlikely that subsequent launches during that session will be slowed. So far, reports of extremely slow launches in Sequoia appear less common, so there may also be benefits to updating macOS if that’s feasible.

Conclusions

  • The most likely cause of extremely slow app launches is security checking of frameworks and similar within the app bundle, when cached checks aren’t available.
  • Time taken for those checks is dependent on the size of the protected code being checked, and is most probably attributable to the computation of SHA-256 hashes.
  • Pre-warming the app by launching it early in each session is a feasible strategy to avoid delays in launching it later.
  • Although these extreme delays appear to be most common on Intel Macs, they can still affect older Apple silicon chips such as the M1 Pro.

I’m very grateful to Alain and Kristian who have generously run tests and provided their logs for my analysis, and their discussion.

How checking hashes can slow your Mac

By: hoakley
28 April 2025 at 14:30

Comparing Macs by their clock speed, the frequency of their CPU, can mislead badly. Because my Intel Mac has 8 cores running at 3.2 GHz doesn’t bring it close to the 6 P-cores running at 4.1 GHz in my M3 Pro, or even its 6 E-cores ambling along at no more than 2.7 GHz. One everyday function where you may notice this most is in computation of hashes.

Although not something we often choose to do, parts of macOS rely on that, and make the difference apparent in everyday tasks, like opening an app. This article explains how older Macs can perform poorly relative to Apple silicon models, and how that can affect you. It also brings an update to my integrity-checking utility Dintch that you may find interesting.

Checking hashes

Although we associate code signing with security certificates, as I pointed out in my brief history, in recent years its use has come to focus more on CDHashes within the signature, as a means of identification and verification of executable code.

Checking the integrity of files such as those containing executable code is simplest performed using a checksum, by adding all the data together as if it were mere numbers, using modular arithmetic, and comparing that sum with what’s expected. The snag with doing that using checksums is that they can all too easily result in false negatives, and fail to detect changes. More sophisticated checksums (Fletcher 64) are used to verify file system metadata in APFS because of their ease and speed of computation, but they’re not strong enough to use for security.

Their replacement for security purposes is the family of hash functions known as Secure Hash Algorithms. SHA-1 uses 160 bits, but was withdrawn from use after weaknesses were discovered, and replaced in most uses by SHA-256. That uses a more complex method to generate a 256-bit number using a one-way process, so there’s no way you can tell what the original contained from its hash, and each hash can fairly safely be assumed to be unique.

Inside each macOS code signature is a data structure containing SHA-256 hashes for all the code and other data protected by that signature, and that code directory is itself hashed to produce Code Directory Hashes, CDHashes (or cdhashes if you prefer). macOS security systems check the integrity of the code directory by comparing its saved CDHashes with fresh hashes of its contents, and can check the integrity of the code itself by comparing its hashes in the code directory with fresh hashes of the contents. There’s a lot of hashing going on.

Hashing performance

We tend to take hashing for granted and assume that it just happens almost instantly. In fact, even when optimised in the likes of CryptoKit in macOS it’s computationally intensive, and throughput can be significantly slower than that of reading data from a fast SSD. To compare SHA-256 hashing performance on a range of Macs, I have modified my free file integrity utility Dintch to report the time taken by hashing operations. Details of this new version are given below.

I created files of standard sizes between 1 MB and 10 GB, tagged them with SHA-256 hashes using Dintch, then checked those hashes with time measurements. To check hashes, Dintch streams a file from disk while CryptoKit computes the hash on that stream, as shown in the source code in the Appendix at the end. Another advantage of using Dintch is that it has control over which CPU cores it’s run on, in Apple silicon Macs, by setting the QoS for that thread.

My iMac Pro proved far slower than either a MacBook Pro M3 Pro or Mac mini M4 Pro, even when the hashing was performed at minimum QoS and run on the E cores of the latter two Macs. At its fastest, with a high QoS, the iMac Pro took 2.4 seconds to hash a 1 GB file, which took only 2.1 seconds on the M3 Pro’s E cores, or 0.34 seconds on the M4 Pro’s P cores. Other results are illustrated in the chart below.

This shows times required to hash files of between 1 and 100 MB size, with linear regressions fitted with additional values for 1 and 10 GB. Points and lines in red are those at high QoS, and those in blue at low QoS. The iMac Pro points are filled circles, those for the M3 Pro crosses, and the M4 Pro open diamonds. The upper pair of lines are from the iMac Pro, and slower than even the E cores in the M3 Pro. The almost coincident lines closest to the X axis are those for P cores in the M3 Pro and M4 Pro.

Converting those regression coefficients into hashing rates gives the following:

  • iMac Pro 0.41 / 0.18 GB/s (fast / slow)
  • M3 Pro 2.71 / 0.52 GB/s
  • M4 Pro 2.92 / 0.64 GB/s.

All of these rates are slower than the read speeds of the SSD containing the files being hashed, indicating that performance limitation is the result of the hash computation.

Mitigations

When looking at slow launches of apps, I recently used Pages and Calibre as examples. If they were required to undergo full verification of their stored CDHashes, including the whole protected contents, that would take significant time. Considering just the Frameworks folder in Pages’ app bundle, that amounts to nearly 250 MB, taking well over 0.5 seconds running at high QoS on an iMac Pro, but that could be less than 0.1 seconds on an M4 Pro.

Although the Mach-O binaries in Calibre’s MacOS folder are only just over 2 MB in size, its Frameworks folder is almost 1 GB, so would take over 2.5 seconds on the iMac Pro, but only around 0.4 seconds on the M4 Pro.

In practice such hefty computational tasks are avoided. macOS uses cached values for hashes as much as possible, and doesn’t appear to verify the whole protected contents of its CDHashes even during first run security checks. Hashes for protected contents are also saved in the code directory in per-page form, so that each page can be verified individually, without having to compute the hash for an entire Mach-O binary. As explained in Apple’s Tech Note, “This allows the system to run a code-signed executable and check its code signature lazily (in the computer science sense of that word).” “macOS doesn’t always check code as it’s paged in.”

But when a hash does need to be verified, expect substantial performance differences between Macs that might appear to have similar numbers of cores and clock frequencies.

Dintch 1.7

Dintch is one of three components of my file integrity system. This new version is recommended for those running Big Sur and later, as it optimises code for those more recent versions of macOS, and for Apple silicon Macs.

In addition, this new version 1.7 shows the time taken to check each file that has already been tagged with a SHA-256 hash. Even if you don’t want to use the app to protect file integrity, it can provide you with SHA-256 performance figures. To do that, click on the Tag button and select the folder whose files you want to use in the test. Once they have been tagged, tick the Verbose option, click the Check button and select that folder. The app’s window will show the time taken to check the SHA-256 hash on each tagged file in that folder. You can then compare the performance of your Mac against the figures I have given above for my Macs.

Dintch 1.7 is now available from here, for Big Sur and later: dintch17
from Downloads above, its Product Page, and via its auto-update mechanism.

Its siblings are:
Fintch 1.3 (Universal App for High Sierra to Sequoia) for drag-and-drop use on small folders and individual files, and
cintch 3 (Universal binary for Big Sur to Sequoia), a command tool with similar features.
Fuller details are on their Product Page.

Enjoy!

Reference

Apple TN3126: Inside Code Signing: Hashes

Appendix: Source code

This uses CryptoKit to hash a file stream using a buffer of size theBufferSize.

func getHash(thePath: String) -> SHA256Digest? {
var hasher = SHA256()
var theDigest: SHA256Digest?
if let theFileStream = InputStream(fileAtPath: thePath) {
theFileStream.open()
let theBuffer = UnsafeMutablePointer.allocate(capacity: theBufferSize)
while theFileStream.hasBytesAvailable {
let read = theFileStream.read(theBuffer, maxLength: theBufferSize)
if read >= 0 {
let theBufferPointer = UnsafeRawBufferPointer(start: theBuffer, count: read)
hasher.update(bufferPointer: theBufferPointer)
}
}
theDigest = hasher.finalize()
theBuffer.deallocate()
theFileStream.close()
}
return theDigest
}

Last Week on My Mac: Gone to launch

By: hoakley
27 April 2025 at 15:00

I looked in the macOS cupboard last week and opened yet another can of worms, in those Macs that take many seconds or even minutes to launch some apps. In common with many other thorny problems, there’s little consensus about many of the details, other than this mostly affecting Intel Macs running macOS Big Sur or later, although I have seen reports of similar problems in M1 Macs.

Although some have apparently found solutions, such as Jeff Johnson’s recommendation to disable SIP, the only consistent answer appears to be moving the app to another location and moving it back again. Even that’s only a temporary measure, and sooner or later those apps will launch slowly again.

Last week I thought I might have a chance to get to the bottom of the problem, when Kristian kindly sent me some log extracts from his Mac’s slow launches, and patterns started to emerge. But like every other good can of worms, the deeper I dig into the can, the more worms there are. This article is a convenient opportunity to reveal some of those.

What takes time?

Looking at three test launches of Pages, greatest differences were seen in the time taken to check the many frameworks in the app bundle, shown in purple in the bar chart below.

In the slow launch provided by Kristian, total time was just over 4 seconds, within which checking frameworks took 3.2 seconds, 80% of launch time. A similarly slow launch in my test VM took just over 3 seconds, with 2.5 seconds on frameworks, while a fast launch completed in just over half a second, with less than 0.1 seconds spent in framework checks.

Cache misses and SIP

What I didn’t mention in those log extracts was the role of cache misses in framework checks in the slower launches. At that stage, it looked as if differences were down to whether each framework had to be checked from scratch as its previous assessment couldn’t be found in cached security checks.

I have therefore repeated the VM tests in two further conditions, with SIP disabled, and with Gatekeeper/XProtect checks disabled.

When launched after a restart, with SIP fully disabled, Pages did indeed launch quickly, in a total time of 0.34 seconds with less than 0.05 seconds checking frameworks. However, disabling Gatekeeper resulted in a total launch time that was only slightly quicker than normal at 2.4 seconds, and 1.8 seconds checking frameworks.

Behaviour with SIP turned off was odd, in that no Gatekeeper process assessment was recorded, and there was no mention of any cache misses for framework checks. In contrast, when Gatekeeper was turned off, there was a Gatekeeper process assessment using a previous code evaluation, and cache misses were recorded for all the frameworks checked. My previous experience with disabling Gatekeeper is that its checks still take place and are logged normally, but their results are ignored, so that wasn’t as paradoxical as it might appear.

Calibre

Pages is an atypical example, being an App Store app signed by Apple, so I then turned to the other log record provided by Kristian, from Calibre. This is remarkable for having 68 frameworks, and took a total of 4.6 seconds to launch on his Mac, with nearly 4.4 seconds of that spent checking those frameworks, all of which were cache misses and reported with constraint violations by amfid.

When I tried repeating this in my test VM and with SIP or Gatekeeper disabled, results became far more complicated, so I have summarised them in a table.

Rows in this table record results from each of six launches of the current release of Calibre. The first is that obtained by Kristian on his Mac, the remainder are all obtained from a 4-core VM running macOS 14.7.5 on a Mac mini M4 Pro. Those were:

  • first run after installation, with the quarantine flag set;
  • second run a minute or so after that first run, without a restart;
  • third run after closing the VM and starting it up again;
  • after downgrading security to permissive by disabling SIP completely;
  • after disabling Gatekeeper/XProtect checks, but still at Full Security, with SIP enabled.

Total launch time is measured from the initial click in the Finder to the app loading its preferences. Time checking frameworks is measured from the start of the first check called by amfid on one of Calibre’s frameworks, to the completion of the its check on the last framework. XProtect scan is measured between the announcement of the start of Gatekeeper’s XProtect scan, and its completion.

The fastest launch by far was in the second run, when none of the checks were performed, there were hardly any log entries from security subsystems or processes, and it completed in 0.158 seconds. The slowest was the first run, when the XProtect scan took over 6 seconds, and accounted for 77% of the total launch time of almost 8 seconds.

Remaining results fall into two groups: Kristian’s original slow launch, with almost 4.4 seconds taken checking frameworks, all of them being recorded as cache misses, and the three VM tests. The latter were remarkable as launch time was uniform at just over 0.5 seconds, and a fifth of the time spent checking frameworks. That occurred because macOS proceeded to run the app before checking of frameworks was complete. In those three tests, no cache misses were recorded, and time was independent of whether a previous Gatekeeper assessment was used. Two of them even included brief local checks against the app’s notarization ticket.

Neither disabling SIP nor disabling Gatekeeper had any effect on the time taken to check frameworks, nor on the total time taken to launch the app.

Diagnosing slow launches

I have given details of my results because, if there’s one thing they demonstrate, it’s the complexity of determining app launch times. In Pages, much of the delay seems to result from cache misses slowing framework checks, and disabling SIP restored rapid launching, as Jeff Johnson reported. In Calibre, launches proceed without waiting for framework checks to complete, and disabling SIP doesn’t result in any acceleration.

This reflects the complexity of app launch. I’ve here concentrated on security checks, as they have been most commonly blamed for this problem. Other processes that have to be completed during launch include:

  • LaunchServices registration and coordination
  • RunningBoard registration and resource management
  • TCC privacy controls
  • sandbox and container preparation
  • iCloud connection
  • checks for app updates
  • all other app initialisations

several of which can involve their own security checks.

Although most of those are well recorded in log entries, disentangling them when watching Activity Monitor or in spindumps is more demanding, and there’s ample opportunity to gain false impressions.

But none of these tests or logs represent what happens in the slowest of launches that can take several minutes. Even the 8 seconds total launch time taken on Calibre’s first run pales in comparison to the 300 seconds that some report. There’s a difference of two orders of magnitude, suggesting that really long launch times are different from all the observations and measurements here. There must be something seriously wrong for an app to take several minutes to launch. Investigating that is also a great challenge.

Tackling slow launching

Results above demonstrate how subtle factors can result in fairly modest apps launching in anything from less than 0.2 to almost 8 seconds. If an app regularly takes several seconds to launch, and that irks you, try pre-warming it first. Longer times are more likely to occur after a Mac has been started up from cold, and apps usually launch fastest when they’ve already been run in the same session. Even if your Mac doesn’t have sufficient memory to leave that app running, that could make subsequent launches quicker.

If an app not infrequently takes more than 30 seconds to launch, then finding the cause becomes more important, and if it takes several minutes, you really need to investigate further.

Capturing an excerpt of the whole log for several minutes isn’t going to help, but will just overwhelm you with many MB of inscrutable data. If you know your way around spindumps, you may find them helpful, although they too can only cover brief periods of time. There are some basic steps you can take that can also help you diagnose and address performance problems more generally:

  • Measure launch time carefully and record it. Counting icon bounces in the Dock is better than nothing, and gives you an objective measure of time to launch.
  • Try in Safe mode. That disables most customising software, and any improvement in launch times points the finger at extensions, startup items and other software you have installed.
  • Create a new user and see if that account has the same problems. This can detect user-specific customisations that might be the cause.
  • Build an external bootable SSD with a ‘clean’ system, install the app(s) there, start up from it, and see whether it still launches as slowly. You can also try this using a more recent version of macOS, to see whether that helps.

The best way to preserve a record of what happened during a slow launch is to wait until the app is running properly, then perform a sysdiagnose, as that saves much of the log in a logarchive. You can initiate that in the Options … popup menu in Activity Monitor, or by pressing Command-Option-Control Shift-. (period or full stop). That logarchive can be browsed using Ulbow or even Console, but not (yet) LogUI.

I’m also keen to look at log records from one of these much slower launches. If you’re interested in cooperating, please comment below or send me an email (see the About page).

❌
❌