Skip to content

Launch stalls for 10 s on macOS 27 (App Store build): system_profiler call blocks the main thread #178

Open

Description

@wiedem

Description

On macOS 27, about one launch in seven of the Mac App Store build freezes for about 10 seconds before the window becomes usable. The delay is always the same length. It started with the update to macOS 27.0.1; launches were fast before.

The cause is deviceModelPretty(): it runs /usr/sbin/system_profiler -json SPHardwareDataType -detailLevel mini on every launch and waits for it with waitUntilExit() on the main thread. Under the App Sandbox, system_profiler on macOS 27 sometimes takes exactly 10 seconds to finish.

Steps to reproduce

  1. Install the Mac App Store build on macOS 27.
  2. Quit and relaunch Telegram several times.

Expected: the window is usable within a fraction of a second on every launch.
Actual: about one launch in seven freezes for about 10 seconds.

Environment

  • macOS 27.2 (26B123), MacBook Pro (16-inch, 2023), Apple M2 Pro
  • Telegram 13.1.0 (283357), Mac App Store build (sandboxed)

Analysis

Code locations (at 45d13c1):

  • TelegramSystem.swift#L25: deviceFromSystemProfiler() launches system_profiler and calls waitUntilExit() with no timeout. deviceModelPretty() calls it on every invocation; the result is not cached.
  • AppDelegate.swift#L832: deviceModelPretty() is evaluated while building NetworkInitializationArguments, right before SharedAccountContext opens the postbox. The enclosing closure is delivered on the main queue (#L652), so the UI is blocked for the whole wait.
  • ShareViewController.swift#L123: the share extension does the same.

Why system_profiler sometimes takes 10 seconds: since macOS 27, SPHardwareDataType queries the Activation Lock status from com.apple.icloud.findmydeviced. system_profiler inherits Telegram's App Sandbox, which denies that mach lookup. Usually both FindMyDevice calls fail at once and system_profiler exits within about 0.1 s. Sometimes the completion of the second call (isActivationLockedWithCompletion:) never arrives, and the hardware reporter waits for its fixed 10-second timeout. That part is an Apple bug, but it only hurts Telegram because the call is synchronous and on the launch path.

Telegram's own log during a slow launch (paths shortened, times relative to the first line):

+0.000 s   AccountManager initialization took 40.06195068359375 ms
+10.187 s  openPostbox, basePath: …/appstore/account-…/postbox, useCopy: false

System log in the same window, filtered to the system_profiler worker process (same time reference):

+0.052 s   system_profiler[…] FRAMEWORK API: -[FMDFMMManager isActivationLockCapableWithCompletion:]
+0.052 s   kernel  Sandbox: system_profiler(…) deny(1) mach-lookup com.apple.icloud.findmydeviced
+0.052 s   system_profiler[…] XPC error for isActivationLockCapableWithCompletion: 0
+0.052 s   system_profiler[…] FRAMEWORK API: -[FMDFMMManager isActivationLockedWithCompletion:]
+10.054 s  system_profiler[…] (SPPlatformReporter) Timed out waiting for the Activation Lock Status check

In fast launches, XPC error for isActivationLockedWithCompletion: 0 follows immediately and there is no timeout. Telegram continues about 0.1 s after the timeout line.

The stall also reproduces without Telegram, by denying only that lookup:

printf '(version 1)\n(allow default)\n(deny mach-lookup (global-name "com.apple.icloud.findmydeviced"))\n' > deny-fmd.sb
time sandbox-exec -f deny-fmd.sb /usr/sbin/system_profiler -json SPHardwareDataType -detailLevel mini

In 40 runs, one took 10.09 s, with the same log pattern as above.

Suggested fix

The device name does not change while the app is running, so it does not need a subprocess on every launch:

  1. Avoid system_profiler entirely. On Apple Silicon the IORegistry entry IODeviceTree:/product has a product-name property (here: "MacBook Pro (16-inch, 2023)"), and sysctlbyname("machdep.cpu.brand_string") returns the chip ("Apple M2 Pro"). Both are instant and need no subprocess. I have not tested them inside the App Sandbox.
  2. If system_profiler stays: compute the name once, off the main thread, and give the process a short timeout with the existing hw.model fallback.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions