How do I get swift-inspect to work on macOS

After recently hearing about swift-inspect I now wanted to take it for a spin and see what can be gleaned - but I simply can't get it to work;

Anyone who has it working?

E.g. (86026 is a process started by me):

hassila@ice ~> sudo swift-inspect dump-arrays 86026 --fork-corpse
Password:
unable to get task for pid 86026: (os/kern) failure 0x5
Failed to create inspector for process id 86026
hassila@ice ~> csrutil
hassila@ice ~> swift-inspect dump-arrays 86026
unable to get task for pid 86026: (os/kern) failure 0x5
Failed to create inspector for process id 86026
hassila@ice ~> 

I enabled this in case it would help (it didnt'):

I guess I can reboot the machine without SIP, but that is really not desirable...

So anyone who've used it?

1 Like

I suspect that you need to sign the target executable with the com.apple.security.get-task-allow entitlement.

The error message suggests it’s using task_for_pid. Modern versions of macOS severely restrict the use of that call such that it’s only really useful for developers tools, where it’s expected that the target executable be built for debugging.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

2 Likes

Thanks so much, will give it a try as soon as k get the chance next week. Have a great weekend!

No go unfortunately, checking code sign:

hassila@ice ~ [2]> codesign -d --entitlements - /Users/hassila/Library/Developer/Xcode/DerivedData/Ordo-gmejvaknewiunvewtbtnixiriytp/Build/Products/Release/OrdoApp.app/Contents/MacOS/OrdoApp

Executable=/Users/hassila/Library/Developer/Xcode/DerivedData/Ordo-gmejvaknewiunvewtbtnixiriytp/Build/Products/Release/OrdoApp.app/Contents/MacOS/OrdoApp
[Dict]
	[Key] com.apple.security.cs.disable-library-validation
	[Value]
		[Bool] true
	[Key] com.apple.security.get-task-allow
	[Value]
		[Bool] true
hassila@ice ~> 

still same error code...

Xcode adds com.apple.security.get-task-allow to debug builds by default, even if you don't have it in your Entitlements plist, so swift-inspect should work out-of-the box.

I wasn't able to reproduce this failure, even when not using sudo. Perhaps a silly question: are you 100% sure that you typed the PID correctly? I'm asking because the error message when the PID can't be found at all is exactly the same as what you saw, i.e.

unable to get task for pid 12345: (os/kern) failure 0x5
Failed to create inspector for process id 12345

So I’ve tried it on release builds with the entitlement added (as debug builds are close to unusable for most our testing), but perhaps that’s simply not supported? (It’d be great if it was, a large value of introspection would be to. Heck on production systems in controlled environments).

I’m 99.9% sure the pid was entered correctly as I was fairly careful but I can double check again tomorrow when in the office.

I'm not an expert here, but presumably it would be supported on release builds as long as the com.apple.security.get-task-allow entitlement is present.

I found some comments at Block stripping of com.apple.secur… | Apple Developer Forums that suggest that you'd need to set DEPLOYMENT_POSTPROCESSING to NO to get Xcode to avoid stripping out that entitlement for release builds. However, from your listed output above, it looks as if you have already achieved that for your release build.

As far as I know, there's nothing about task_for_pid that checks how the app was built, only how it's signed and what entitlements it has at the time when you try to attach. From what I can tell based on my own testing, the codesign -d --entitlements - you included above should be enough.

I don't know whether the presence of com.apple.security.cs.disable-library-validation is a problem; I realize that it's present for a reason (i.e. your app requires it), but if there's no other answer it might be worth checking whether removing it allows swift-inspect to attach.

1 Like

Hmmm, interesting. task_for_pid works as I explained earlier. To confirm this I created a tiny target process and a tiny inspector tool that calls task_for_pid (code for both at the end) and things work as expected. I can run the target in one window:

% codesign -d --ent - TestSwiftInspect
…
[Dict]
    …
    [Key] com.apple.security.get-task-allow
    [Value]
        [Bool] true
% ./TestSwiftInspect
pid: 21249
waiting… 0
waiting… 1
…

and the inspector in the other:

% ./TaskForPID 21249
21249: 0x1907

and it gets the task control port just fine.

Note I’m using Xcode 15.3 on macOS 14.4, with both projects starting from the macOS > Command Line Tool template.

However, this doesn’t work for swift-inspect )-:

% /usr/bin/swift-inspect dump-concurrency 21249
unable to get task for pid 21249: (os/kern) failure 0x5
Failed to create inspector for process id 21249

And sudo doesn’t help:

% sudo /usr/bin/swift-inspect dump-concurrency 21249
unable to get task for pid 21249: (os/kern) failure 0x5
Failed to create inspector for process id 21249

I suspect this has something to do with the fact that swift-inspect is a system binary, and macOS imposes a bunch of extra security constraints on system binaries.

My standard trick for getting around this is to copy the system binary and re-sign it. Sadly, that doesn’t work in this case:

% cp /usr/bin/swift-inspect .
% codesign -s "Apple Development: …" -f ./swift-inspect 
./swift-inspect: replacing existing signature
% ./swift-inspect dump-concurrency 21249                     
zsh: killed     ./swift-inspect dump-concurrency 21249

This is because swift-inspect is arm64e, not arm64. Apple doesn’t support arm64e for third-party code (outside the kernel). So, I used arch to run the Intel ‘slice’:

% arch -arch x86_64 ./swift-inspect dump-concurrency 21249
TASKS
…
ACTORS
THREADS
  no threads with active tasks

and that actually worked!

Clearly this is less than ideal and I encourage you to file a bug about it. Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple


import Foundation

func main() async {
    print("pid: \(getpid())")
    var counter = 0
    while true {
        print("waiting… \(counter)")
        try! await Task.sleep(for: .seconds(5))
        counter += 1
    }
}

await main()
import Foundation

extension CommandLine {
    … elided …
}

func mainThrowing() throws {
    let args = CommandLine.arguments.dropFirst()
    let pids = args.compactMap { pid_t($0) }
    guard !pids.isEmpty, pids.count == args.count else {
        throw CommandLine.ToolError.usage
    }
    for pid in pids {
        var taskPort = mach_port_name_t(bitPattern: MACH_PORT_NULL)
        let kr = task_for_pid(mach_task_self_, pid, &taskPort)
        if kr == KERN_SUCCESS {
            // IMPORTANT: This leaks `taskPort`, which doesn’t 
            // matter in this case but be carefuly if you copy
            // this code.
            print("\(pid): 0x\(String(taskPort, radix: 16))")
        } else {
            print("\(pid): error: \(kr)")
        }
    }
}

func main() -> Never {
    do {
        try mainThrowing()
        exit(EXIT_SUCCESS)
    } catch CommandLine.ToolError.usage {
        print("usage: \(CommandLine.progName) pid...", to: &CommandLine.stderr)
    } catch CommandLine.ToolError.silent {
        // do nothing
    } catch {
        print("error: \(error)", to: &CommandLine.stderr)
    }
    exit(EXIT_FAILURE)
}

main()
7 Likes

Thank you very much Quinn for those details, and finding the workaround with arch. In fact, I realize now that the machine I was doing my testing on is an Intel Mac, so that explains why swift-inspect worked in every test I did.

3 Likes

Thanks so much both for the help, out of office but will definitely file a report and try to give the workaround a spin.

1 Like

Won't have access until next week to try workaround, but I've filed FB13694901 for this issue, thanks!

1 Like

So, I did get the chance to play a bit with the workaround (followed your step with a local copy that I codesigned) - some good and some bad news;

Of the subcommands supported by swift-inspect:

SUBCOMMANDS:
  dump-conformance-cache  Print the contents of the target's protocol conformance cache.
  dump-raw-metadata       Print the target's metadata allocations.
  dump-generic-metadata   Print the target's generic metadata allocations.
  dump-cache-nodes        Print the target's metadata cache nodes.
  dump-arrays             Print information about array objects in the target.
  dump-concurrency        Print information about the target's concurrency runtime.

  See 'swift-inspect help <subcommand>' for detailed help.

I could get dump-conformance-cache to work and provide output (so it worked for this case);

The metadata stuff and cache-nodes weren't "enabled" which might be expected for a release build?:

hassila@ice ~/g/ordo (main)> arch -arch x86_64 ./swift-inspect dump-generic-metadata 1223
Error: remote process does not have metadata allocation iteration enabled
hassila@ice ~/g/ordo (main) [1]> arch -arch x86_64 ./swift-inspect dump-raw-metadata 1223
Error: remote process does not have metadata allocation iteration enabled
hassila@ice ~/g/ordo (main) [1]> arch -arch x86_64 ./swift-inspect dump-cache-nodes 1223
Address	Tag	Tag Name	Size	Left	Right
Error: remote process does not have metadata allocation iteration enabled

While finally dump-arrays (and concurrency, which was my main interest) both fails:

hassila@ice ~/g/ordo (main) [1]> arch -arch x86_64 ./swift-inspect dump-arrays 1223
Address	Size	Count	Is Class
2024-03-25 18:45:37.176 swift-inspect[2799:16189556] libsystem_malloc.dylib in target process is different than in analysis process, which can cause problems analyzing memory.  You may need to relaunch the target process and analysis process to get the new version of the framework.  The dyld shared cache may also be out of date, which could affect system performance.  To update the dyld shared cache, run 'sudo update_dyld_shared_cache' and reboot.

fish: Job 1, 'arch -arch x86_64 ./swift-ins...' terminated by signal SIGBUS (Misaligned address error)
hassila@ice ~/g/ordo (main) [SIGBUS]> arch -arch x86_64 ./swift-inspect dump-concurrency 1223
TASKS
2024-03-25 18:45:58.867 swift-inspect[2929:16190092] libsystem_malloc.dylib in target process is different than in analysis process, which can cause problems analyzing memory.  You may need to relaunch the target process and analysis process to get the new version of the framework.  The dyld shared cache may also be out of date, which could affect system performance.  To update the dyld shared cache, run 'sudo update_dyld_shared_cache' and reboot.

fish: Job 1, 'arch -arch x86_64 ./swift-ins...' terminated by signal SIGBUS (Misaligned address error)

There isn't a direct dependency on libsystem_malloc.dylib (but I wonder if that is a red herring):

hassila@ice ~/g/ordo (main) [SIGBUS]> otool -L ./swift-inspect 
./swift-inspect:
	/usr/lib/swift/libswiftRemoteMirror.dylib (compatibility version 1.0.0, current version 0.0.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 2419.0.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1345.100.2)
	/usr/lib/swift/libswiftCore.dylib (compatibility version 1.0.0, current version 5.10.0)
	/usr/lib/swift/libswiftCoreFoundation.dylib (compatibility version 1.0.0, current version 120.100.0, weak)
	/usr/lib/swift/libswiftDarwin.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftDispatch.dylib (compatibility version 1.0.0, current version 41.0.0, weak)
	/usr/lib/swift/libswiftIOKit.dylib (compatibility version 1.0.0, current version 1.0.0, weak)
	/usr/lib/swift/libswiftObjectiveC.dylib (compatibility version 1.0.0, current version 8.0.0, weak)
	/usr/lib/swift/libswiftXPC.dylib (compatibility version 1.0.0, current version 36.100.7, weak)
	/usr/lib/swift/libswift_Concurrency.dylib (compatibility version 1.0.0, current version 5.10.0)

and neither for the app in question:

hassila@ice ~> otool -L /Users/hassila/Library/Developer/Xcode/DerivedData/Ordo-gmejvaknewiunvewtbtnixiriytp/Build/Products/Release/ORdoApp.app/Contents/MacOS/OrdoApp
/Users/hassila/Library/Developer/Xcode/DerivedData/Ordo-gmejvaknewiunvewtbtnixiriytp/Build/Products/Release/ORdoApp.app/Contents/MacOS/OrdoApp:
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1700.255.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 2420.0.0)
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.12)
	/usr/lib/libcurl.4.dylib (compatibility version 7.0.0, current version 9.0.0)
	/usr/lib/libsasl2.2.dylib (compatibility version 3.0.0, current version 3.15.0)
	@rpath/Algorithms_-79BF82D738C97CDC_PackageProduct.framework/Versions/A/Algorithms_-79BF82D738C97CDC_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/OrdoInternals.framework/Versions/A/OrdoInternals (compatibility version 63.1.0, current version 63.1.0)
	@rpath/OrdoEssentials_14A04D918944EDBD_PackageProduct.framework/Versions/A/OrdoEssentials_14A04D918944EDBD_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/OrdoEssentials.framework/Versions/A/OrdoEssentials (compatibility version 63.1.0, current version 63.1.0)
	@rpath/OrdoObjectSupport.framework/Versions/A/OrdoObjectSupport (compatibility version 1.0.0, current version 1.0.0)
	@rpath/PredicateEditor_-61AADD5F1054C4F9_PackageProduct.framework/Versions/A/PredicateEditor_-61AADD5F1054C4F9_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/Sparkle.framework/Versions/B/Sparkle (compatibility version 1.6.0, current version 2.5.2)
	@rpath/AsyncAlgorithms_-33263E09A32564D9_PackageProduct.framework/Versions/A/AsyncAlgorithms_-33263E09A32564D9_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/Collections_47BB0D94F2814A8A_PackageProduct.framework/Versions/A/Collections_47BB0D94F2814A8A_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/OrdoConfig.framework/Versions/A/OrdoConfig (compatibility version 63.1.0, current version 63.1.0)
	@rpath/OrdoPublic_3CE1D3C59D0CD3FD_PackageProduct.framework/Versions/A/OrdoPublic_3CE1D3C59D0CD3FD_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/OrdoPublic.framework/Versions/A/OrdoPublic (compatibility version 63.1.0, current version 63.1.0)
	@rpath/Atomics_301F3DE065572157_PackageProduct.framework/Versions/A/Atomics_301F3DE065572157_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/Logging_35C93DA702091ECE_PackageProduct.framework/Versions/A/Logging_35C93DA702091ECE_PackageProduct (compatibility version 0.0.0, current version 0.0.0)
	@rpath/PluginSupport.framework/Versions/A/PluginSupport (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1345.100.2)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2487.50.124)
	/System/Library/Frameworks/Combine.framework/Versions/A/Combine (compatibility version 1.0.0, current version 311.0.0)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 2420.0.0)
	/System/Library/Frameworks/Network.framework/Versions/A/Network (compatibility version 1.0.0, current version 1.0.0, weak)
	/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61123.100.169)
	/System/Library/Frameworks/SwiftUI.framework/Versions/A/SwiftUI (compatibility version 1.0.0, current version 5.4.38)
	/usr/lib/swift/libswiftCore.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftCoreAudio.dylib (compatibility version 1.0.0, current version 6.0.0, weak)
	/usr/lib/swift/libswiftCoreFoundation.dylib (compatibility version 1.0.0, current version 120.100.0, weak)
	/usr/lib/swift/libswiftCoreImage.dylib (compatibility version 1.0.0, current version 2.0.0, weak)
	/usr/lib/swift/libswiftCoreMedia.dylib (compatibility version 1.0.0, current version 3100.20.2, weak)
	/usr/lib/swift/libswiftDarwin.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftDispatch.dylib (compatibility version 1.0.0, current version 41.0.0)
	/usr/lib/swift/libswiftDistributed.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftIOKit.dylib (compatibility version 1.0.0, current version 1.0.0, weak)
	/usr/lib/swift/libswiftMetal.dylib (compatibility version 1.0.0, current version 343.14.0, weak)
	/usr/lib/swift/libswiftOSLog.dylib (compatibility version 1.0.0, current version 4.0.0, weak)
	/usr/lib/swift/libswiftObjectiveC.dylib (compatibility version 1.0.0, current version 8.0.0, weak)
	/usr/lib/swift/libswiftObservation.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftQuartzCore.dylib (compatibility version 1.0.0, current version 3.0.0, weak)
	/usr/lib/swift/libswiftRegexBuilder.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftUniformTypeIdentifiers.dylib (compatibility version 1.0.0, current version 791.2.8, weak)
	/usr/lib/swift/libswiftXPC.dylib (compatibility version 1.0.0, current version 36.100.7, weak)
	/usr/lib/swift/libswift_Concurrency.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswift_StringProcessing.dylib (compatibility version 1.0.0, current version 0.0.0)
	/usr/lib/swift/libswiftos.dylib (compatibility version 1.0.0, current version 1049.100.4)

Just checking, but this is a macOS app, right, and not a Catalyst app?

Yeah, macOS native - swift and SwiftUI 100%

Do you have the Hardened Runtime setting enabled in Xcode? (If so, try turning it off…)

I think the ideal would be that we add some entitlements to swift-inspect at our end to make this work without that, but I think that might fix your problem for now.

1 Like

Thanks @al45tair - one more step forward, but still not working quite - we had hardened runtime turned on for the main app target and now tried consistently turn it off for all targets in Xcode, and now dump-array provided some output before crashing:

  See 'swift-inspect help <subcommand>' for detailed help.
hassila@ice ~/g/ordo (main)> arch -arch x86_64 ./swift-inspect dump-arrays 9681
Address	Size	Count	Is Class
2024-03-26 16:49:27.160 swift-inspect[10126:18691479] libsystem_malloc.dylib in target process is different than in analysis process, which can cause problems analyzing memory.  You may need to relaunch the target process and analysis process to get the new version of the framework.  The dyld shared cache may also be out of date, which could affect system performance.  To update the dyld shared cache, run 'sudo update_dyld_shared_cache' and reboot.

0x600000100120	144	1	false
0x600000100870	144	1	false
0x600000100c60	144	1	false
0x600000100f30	144	1	false
0x600000100fc0	144	1	false
0x6000001014d0	144	1	false
0x600000101b00	144	1	false
0x600000102010	144	1	false
0x600000200280	160	3	false
0x600000200460	160	1	false
0x600000200c80	160	3	false
0x600000201180	160	5	false
0x600000201220	160	1	false
0x600000201400	160	1	false
0x600000201540	160	1	false
0x6000002015e0	160	1	false
0x6000002023a0	160	1	false
0x600000202440	160	1	false
0x600000202580	160	1	false
0x600000202620	160	10	false
0x600000202760	160	1	false
0x600000202940	160	5	false
0x6000002029e0	160	5	false
0x600000202a80	160	5	false
0x600000202b20	160	5	false
0x600000202bc0	160	5	false
0x600000202c60	160	5	false
0x600000202d00	160	5	false
0x600000202da0	160	3	false
0x600000202e40	160	5	false
0x600000202ee0	160	5	false
0x6000002033e0	160	1	false
0x6000002035c0	160	5	false
0x6000002037a0	160	1	false
0x600000203840	160	1	false
0x600000203980	160	1	false
0x600000203a20	160	1	false
0x600000203b60	160	1	false
0x600000203c00	160	1	false
0x600000203d40	160	1	false
0x600000203de0	160	1	false
0x600000203f20	160	1	false
0x600000601080	192	4	false
0x600000601140	192	4	false
0x600000a007e0	224	6	false
0x600000a008c0	224	6	false
0x600000a00d20	224	6	false
0x600000a00e00	224	6	false
0x600000a02300	224	6	false
0x600000a024c0	224	6	false
0x600000a039c0	224	6	false
0x600000a03d40	224	6	false
0x600000a03e20	224	6	false
0x600000a03f00	224	6	false
0x600000e00000	256	1	false
0x600000e00100	256	1	false
0x600000e00200	256	1	false
0x600000e00300	256	1	false
0x600000e00400	256	1	false
0x600000e00500	256	1	false
0x600000e00600	256	1	false
0x600000e00700	256	1	false
0x600000e00800	256	1	false
0x600000e00900	256	1	false
0x600000e00a00	256	1	false
0x600000e00b00	256	1	false
0x600000e00d00	256	1	false
0x600000e00e00	256	1	false
0x600000e00f00	256	1	false
0x600000e01000	256	1	false
0x600000e01100	256	1	false
0x600000e01300	256	1	false
0x600000e01400	256	1	false
0x600000e01500	256	1	false
0x600000e01600	256	1	false
0x600000f02b00	256	3	false
0x600000f03200	256	1	false
0x600000f03300	256	1	false
 fish: Job 1, 'arch -arch x86_64 ./swift-ins...' terminated by signal SIGBUS (Misaligned address error)

While dump-concurrency still crashes immediately:

hassila@ice ~/g/ordo (main) [SIGKILL]> arch -arch x86_64 ./swift-inspect dump-concurrency 9681
TASKS
2024-03-26 16:51:23.310 swift-inspect[10857:18694893] libsystem_malloc.dylib in target process is different than in analysis process, which can cause problems analyzing memory.  You may need to relaunch the target process and analysis process to get the new version of the framework.  The dyld shared cache may also be out of date, which could affect system performance.  To update the dyld shared cache, run 'sudo update_dyld_shared_cache' and reboot.

fish: Job 1, 'arch -arch x86_64 ./swift-ins...' terminated by signal SIGBUS (Misaligned address error)
hassila@ice ~/g/ordo (main) [SIGBUS]> 

I wonder if that's related to the warning you're seeing about libsystem_malloc.dylib being different. Oh, hang on, you ran with -arch x86_64, and you're presumably inspecting an ARM64 process? That might well be the cause of the crash.

Yeah, but that workaround was needed as it otherwise crashes immediately - see @eskimo s comment above...

(and yes, we're inspecting an ARM process, we don't have any x86 macOS machines any more really)

OK, so what happens if you don't copy the swift-inspect binary, but run the original one, without arch -arch x86_64, and after you've turned off Hardened Runtime. I think that should work (the refusal to get the task port is happening because of the hardened runtime setting, I believe).