Still same In Xcode 12.4 (and probably forever).
But attaching seems work fine, if you know the trick:
Call waitForDebugger(...) method (somewhere you're sure to be executed, by step #5), and place breakpoint after that (at least one).
Run App normally (or with debugger).
From within Xcode's "Debug" menu, click the "Attach to Process by PID or Name..." entry.
In resulting dialog, write your extension-target's Name ONLY (instead of bundle-id), and click "Attach".
Start your extension, so that it triggers step #1's logic (and later breakpoint).
Now Xcode should be attached and paused on breakpoint; In "Show the Debug navigator" tab (of left sidebar), select extension-process's "Memory" section, then click "Profile in instruments" button.
In resulting dialog, click "Profile" button, at last profiler did open, but go back to Xcode and resume the execution (remember was paused on breakpoint).
Wait for Debugger (Swift 5 Example)
public static func isDebuggerAttached() -> Bool {
var info = kinfo_proc()
var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
var size = MemoryLayout.stride(ofValue: info)
let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
assert(junk == 0, "sysctl failed")
return (info.kp_proc.p_flag & P_TRACED) != 0
}
@discardableResult
public static func waitForDebugger(_ timeout: Int = 30000) -> Bool {
var now: UInt64 = DispatchTime.now().uptimeNanoseconds
let begin = now
repeat {
if isDebuggerAttached() {
// Wait a little bit longer,
// because early breakpoints may still not work.
Thread.sleep(forTimeInterval: 3.0)
return true
}
Thread.sleep(forTimeInterval: 0.1)
now = DispatchTime.now().uptimeNanoseconds
} while Double(now - begin) / 1000000.0 < Double(timeout);
return false;
}
Note that:
Once the profiler is attached, for some reason the 15MB memory limit (or whatever) gets removed, and App takes about 3 times more memory!?
Just ignore this profiler issues, and somehow count real memory usages :-(