Using Processor Trace on Non-Xcode Built Binary

Hiya folks! I'm David and I work on rust-analyzer, which is a language server for Rust similar to sourcekit-lsp. I'm using the new Instruments profiling tooling functionality in Xcode 16.3 and Xcode 26 (Processor Trace and CPU Counters) to profile our trait solver/type checker. While I've been able to use the new CPU Counters instrument successfully (the CPU Bottleneck feature is incredible! Props to the team!), I've been unable to make use of the Processor Trace instrument.

Instruments gives me the error message "Processor Trace cannot profile this process without proper permissions". The diagnostic suggests adding the com.apple.security-get-task-allow entitlement to the code I'm trying to profile, or ensure that the build setting CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES is enabled in Xcode.

Unfortunately, I don't know how I can add that entitlement to a self-signed binary produced by Cargo and I'm not using Xcode for somewhat obvious reasons.

Here's some information about my setup:

  • Instruments Version 26.0 (17A5241e)
  • I'm on an 14" MacBook Pro with M4 Pro. It's running macOS Version 26.0 Beta (25A5295e).
  • I've enabled the "Processor Trace" feature in "Developer Tools" and even added the Instruments application to "Developer Tools".
  • As a last-ditch effort before posting this, I disabled SIP on my Mac. Didn't help.

To reproduce my issue:

  1. Get Rust via https://rustup.rs/.
  2. Clone rust-analyzer: git clone https://github.com/rust-lang/rust-analyzer.git.
  3. cd rust-analyzer
  4. Run cargo test --package hir-ty --lib --profile=dev-rel -- tests::incremental::add_struct_invalidates_trait_solve --exact --show-output. By default, this command will output a bunch of build progress with the output containing something like Running unittests src/lib.rs (target/dev-rel/deps/hir_ty-f1dbf1b1d36575fe).
  5. I take the absolute path of that hir_ty-$SOME-HASH string (in my case, it looks like /Users/dbarsky/Developer/rust-analyzer/target/dev-rel/deps/hir_ty-f1dbf1b1d36575fe) and add it to the "Launch" profile. To the arguments section, I add --exact tests::incremental::add_struct_invalidates_trait_solve.
  6. I then try to record/profile via Instruments, but then I get the error message I shared above.

Below is output of codesign -dvvv:

❯ codesign -dvvv target/dev-rel/deps/hir_ty-f1dbf1b1d36575fe
Executable=/Users/dbarsky/Developer/rust-analyzer/target/dev-rel/deps/hir_ty-f1dbf1b1d36575fe
Identifier=hir_ty-f1dbf1b1d36575fe
Format=Mach-O thin (arm64)
CodeDirectory v=20400 size=140368 flags=0x20002(adhoc,linker-signed) hashes=4383+0 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=99e96c8622c7e20518617c66a7d4144dc0daef28
CandidateCDHashFull sha256=99e96c8622c7e20518617c66a7d4144dc0daef28f22fac013c28a784571ce1df
Hash choices=sha256
CMSDigest=99e96c8622c7e20518617c66a7d4144dc0daef28f22fac013c28a784571ce1df
CMSDigestType=2
CDHash=99e96c8622c7e20518617c66a7d4144dc0daef28
Signature=adhoc
Info.plist=not bound
TeamIdentifier=not set
Sealed Resources=none
Internal requirements=none

Any tips would be welcome! Additionally—and perhaps somewhat naively—I think I'd expect the Processor Trace instrument to just work with an adhoc-signed binary, as lldb and friends largely do—I'm not sure that such a high barrier for CPU perf counters is warranted, especially on an adhoc-signed binary.

Answered by DTS Engineer in 847151022

I’m not able to help you with third-party tools, so I’m going to base my response on how you would achieve this goal when using Clang directly from Terminal. I’m hoping that you can map this to your third-party tooling.

Also, I’m basing my response on the trivial test case described in Investigating Third-Party IDE Integration Problems. The final point of that is a built executable with no entitlements:

% codesign -d --entitlements - hello 
Executable=/Users/quinn/Test/hello
%

To add the get-task-allow entitlement, first create a property list with the right values:

% plutil -create xml1 hello.entitlements
% plutil -insert 'com\.apple\.security\.get-task-allow' -bool true hello.entitlements 
% cat hello.entitlements 
…
<dict>
	<key>com.apple.security.get-task-allow</key>
	<true/>
</dict>
</plist>

Note Entitlement files are XML property lists. While you can edit these as text, I generally recommend that you use our tools (plutil and also PlistBuddy; both have man pages) because it’s easy to mess things up if you edit them by hand.

Now re-sign the tool:

% codesign -s - -f --entitlements hello.entitlements hello
hello: replacing existing signature

Note that I’m using -s -, which applies an ad-hoc signature, that is, a signature with no associated certificate [1]. This is standard practice when working with open source tooling [2], but it’s not appropriate for a product that you want to ship to a wide range of users [3].

I’m also using -f, to tell codesign to replace the existing signature [4].

Finally, dump its entitlements again, just to be sure:

% codesign -d --entitlements - hello                              
…
[Dict]
	[Key] com.apple.security.get-task-allow
	[Value]
		[Bool] true

I think this’ll be sufficient to get you up’n’running, but let me know if you hit any snags.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Xcode calls this open Sign to Run Locally.

[2] Apple silicon code must be signed, so the linker automatically applies an ad-hoc signature. You can see this if you dump the hello tool before re-signing it:

% clang -o hello hello.c                
% codesign -d -vvv hello
…
CodeDirectory v=20400 size=382 flags=0x20002(adhoc,linker-signed) hashes=9+0 location=embedded
…

Note the adhoc and linker-signed flags.

If you’re going to re-sign the binary anyway, you can disable linker signing with the -no_adhoc_codesign linker option.

[3] For general advice on how to sign and package Mac products, see:

[4] That is, the signature applied by the linker.

I created a feedback item FB18543729 with an attached sysdiagnose, if that's of any help!

I’m not able to help you with third-party tools, so I’m going to base my response on how you would achieve this goal when using Clang directly from Terminal. I’m hoping that you can map this to your third-party tooling.

Also, I’m basing my response on the trivial test case described in Investigating Third-Party IDE Integration Problems. The final point of that is a built executable with no entitlements:

% codesign -d --entitlements - hello 
Executable=/Users/quinn/Test/hello
%

To add the get-task-allow entitlement, first create a property list with the right values:

% plutil -create xml1 hello.entitlements
% plutil -insert 'com\.apple\.security\.get-task-allow' -bool true hello.entitlements 
% cat hello.entitlements 
…
<dict>
	<key>com.apple.security.get-task-allow</key>
	<true/>
</dict>
</plist>

Note Entitlement files are XML property lists. While you can edit these as text, I generally recommend that you use our tools (plutil and also PlistBuddy; both have man pages) because it’s easy to mess things up if you edit them by hand.

Now re-sign the tool:

% codesign -s - -f --entitlements hello.entitlements hello
hello: replacing existing signature

Note that I’m using -s -, which applies an ad-hoc signature, that is, a signature with no associated certificate [1]. This is standard practice when working with open source tooling [2], but it’s not appropriate for a product that you want to ship to a wide range of users [3].

I’m also using -f, to tell codesign to replace the existing signature [4].

Finally, dump its entitlements again, just to be sure:

% codesign -d --entitlements - hello                              
…
[Dict]
	[Key] com.apple.security.get-task-allow
	[Value]
		[Bool] true

I think this’ll be sufficient to get you up’n’running, but let me know if you hit any snags.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Xcode calls this open Sign to Run Locally.

[2] Apple silicon code must be signed, so the linker automatically applies an ad-hoc signature. You can see this if you dump the hello tool before re-signing it:

% clang -o hello hello.c                
% codesign -d -vvv hello
…
CodeDirectory v=20400 size=382 flags=0x20002(adhoc,linker-signed) hashes=9+0 location=embedded
…

Note the adhoc and linker-signed flags.

If you’re going to re-sign the binary anyway, you can disable linker signing with the -no_adhoc_codesign linker option.

[3] For general advice on how to sign and package Mac products, see:

[4] That is, the signature applied by the linker.

Thanks so much for the response! clang and rustc (by design on Rust' part, to be clear!) are sufficiently similar that it was pretty easy to translate between C++ and Rust!

Your tips/suggestions almost worked for me, except that the binary would be sigkilled'd immediately after launch. I did some rubber-duck debugging using Claude, and it—rather impressively!—pointed out in https://claude.ai/share/5a4ca3ca-9e98-4e2a-b9ae-71b49c6983cf that the entitlement I needed to use was com.apple.security.get-task-allow, not com.apple.security-get-task-allow. Instruments' diagnostic contained a typo! Once I fixed this typo, I was able to use the "Processor Trace" instrument via xctrace. Of course, since this is beta software, which I hit a few bugs, which I'll cover at the end of this post.

Apple silicon code must be signed, so the linker automatically applies an ad-hoc signature. You can see this if you dump the hello tool before re-signing it: [dump redacted]

If you’re going to re-sign the binary anyway, you can disable linker signing with the -no_adhoc_codesign linker option.

I think the Rust compiler sets the adhoc signature somewhere by default, so while I agree it's kinda wasteful to replace it later, it's also not the worst.

but it’s not appropriate for a product that you want to ship to a wide range of users

Yeah, I figured as such. I'm only really using these adhoc binaries for as part of my local development workflow.

The Bugs!

Anyways! I promised a few bug reports, here they are!

Typo in Diagnostics

Instruments and xctrace have a typo in their disagnostics entitlement: they both suggest com.apple.security-get-task-allow instead of com.apple.security.get-task-allow. I spent my morning scratching my head over this. See below for the typo:

❯ xctrace record --template 'Processor Trace' --target-stdout - --launch -- target/dev-rel/deps/hir_ty-f1dbf1b1d36575fe --exact tests::incremental::add_struct_invalidates_trait_solve
Starting recording with the Processor Trace template. Launching process: hir_ty-f1dbf1b1d36575fe.
Ctrl-C to stop the recording
Run issues were detected (trace is still ready to be viewed):
* [Error] Processor Trace cannot profile this process without proper permissions.

        * [Error] Recovery Suggestion: Either:
1. Add the 'com.apple.security-get-task-allow' entitlement to your binary entitlements file, or
2. Make sure the build setting CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES is enabled when building with Xcode.

Recording failed with errors. Saving output file...
Output file saved as: Launch_hir_ty-f1dbf1b1d36575fe_2025-07-02_13.39.40_EBCB3760.trace

OOMs in Instruments

I had to use xctrace record --template 'Processor Trace' --target-stdout - --launch -- hir_ty-f1dbf1b1d36575fe --exact tests::incremental::add_struct_invalidates_trait_solve from my shell instead of launching from Instruments directly, as Instruments ended up using something like 130 GB of RAM, forcing me to restart my Mac. I have only have a paltry 48GB!

An Entitlement for Profiling using Hardware Profiling Features Feels Strange

I would personally expect that a linker-signed, adhoc binary would imply com.apple.security.get-task-allow. Some additional, assorted thoughts:

  • rustc tends to use the system's C compilers to indirectly drive the linker, which means that if there's some feature that the linker should be doing automatically on said platform, the Rust compiler does it. Given that the Rust compiler produces adhoc-signed binaries by default and I can debug those binaries using lldb without any additional entitlements, I'd expect the same of hardware-supported CPU execution tracing (modulo restarting my Mac), especially if the CPU execution tracing in M4 processors is anything similar to similar to Intel's Processor Trace: https://lldb.llvm.org/use/intel_pt.html.
    • A friend pointed out that your phrasing of "That is, the signature applied by the linker" should have clued me into the fact that there's some linker magic happening with ld_prime. Cards on the table, I think there should be a little more magic happening :D.
  • The new profiling functionality is downright magical. It just works with non-Swift/C/Objective-C languages and I feel like it's a shame that end-users need to become deeply familiar with codesigning to use this incredible set of tooling. Heck, I bought the Mac I'm writing this post on two days ago in order to use this feature!

Anyways, thanks so much for your help and let me know if I can provide any additional detail!

the entitlement I needed to use was com.apple.security.get-task-allow, com.apple.security-get-task-allow.

Well, that’s an annoying cascade failure. I copied it from you who copied it from Instruments. But, yeah, I shouldn’t have let that slip past )-: Thanks for the heads up.

I went back and fixed my earlier post, just to avoid further confusion.

I also filed my own bug against Instruments (r. 154940665). Life it too short for me to be chasing problems like this.

Anyways! I promised a few bug reports, here they are!

The best way to get these in front of the folks who can actually enact change is to file them in Feedback Assistant. See Bug Reporting: How and Why? for more on that.

Please post your bug number, just for the record.

Except for the Typo in Diagnostics issue. There’s no need for you to file an additional bug about that, unless you want to be notified of the fix.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks Quinn!

Well, that’s an annoying cascade failure. I copied it from you who copied it from Instruments. But, yeah, I shouldn’t have let that slip past )-: Thanks for the heads up.

No worries! I should've noticed that and it's my first time seeing a typo in a diagnostic like this. It's not something I expect to see.

The best way to get these in front of the folks who can actually enact change is to file them in Feedback Assistant. See Bug Reporting: How and Why? for more on that.

Thanks for the information and sorry for not being familiar with the process of reporting bugs to Apple!

For the bugs, here are my feedback items:

  • The Instruments OOM: FB18583028.
  • The "adhoc-signed binaries shouldn't need an additional entitlement just to be profiled" feedback item: FB18543729.

I submitted both under "Developer Tools & Resources", but I can see an argument for the latter being filed under something closer to "kernel/core OS". Lemme know if you'd like me to file the entitlement feedback there!

Except for the Typo in Diagnostics issue. There’s no need for you to file an additional bug about that, unless you want to be notified of the fix.

I didn't submit a feedback item for the typo; I think your bug report is sufficient :). I'll just keep an eye on the changelog of future Xcode releases!

Using Processor Trace on Non-Xcode Built Binary
 
 
Q