Cannot profile network extension in iOS 10

Hi,


I have been attempting to profile an iOS tunneling network extension and have been running into issues when trying to profile on the iOS 10 GM. I want to use the Leaks and Allocations Instruments templates to monitor memory usage. I set the target device to my phone and the profiling target to my PacketTunnelProvider the and click record. I will then go into the app and tap the button that starts the tunneling connection, and the VPN text appears in the status bar. Meanwhile, on the Mac I sit at a dialog with the following text: "Waiting for com.mycompany.PacketTunnelProvider Please take appropriate action to initiate the launch of 'com.mycompany.PacketTunnelProvider.'"


Steps to Reproduce:


  1. Select the PacketTunnel's targets build scheme in Xcode
  2. Click 'Profile' with my iOS 10 device selected
  3. Instruments gets launched, I choose Leaks or Allocations instruments.
  4. New Instruments trace document opens, click the red record button.
  5. Go into the app to start tunneling VPN go into Setting.app and manually start VPN.
  6. VPN starts and stays connected, but Instruments never starts profiling, instead I still see the dialog: "Waiting for com.mycompany.PacketTunnelProvider Please take appropriate action to initiate the launch of 'com.mycompany.PacketTunnelProvider.'"


I have been profiling this way on iOS 9 without issue, but when trying to profile on an iOS 10 device, I cannot seem to get the profiler to attach to process.


I have filed a radar but I wanted to see if anyone else has a similar issue.


Radar ID: 28195578

Post not yet marked as solved Up vote post of zachhowe Down vote post of zachhowe
4.4k views

Replies

Any news on this issue?


If I try to attach manually to the RUNNING extension, I get this error from instruments:


XPC service name <EXTENSION ID> already under observation for pid: 0

a year again, but has anyone found the answer???

Encounter the same issue, anyone has a solution?

I'm experiencing the same issue in 2018. Is there any solution anyone?

Same issue here, enhancement request open: 47814258 😐

enhancement request open: 47814258

Thanks!

I’ve recently been helping a developer with this in the context of a DTS tech support incident, and I came up with a technique that, while not a full workaround, can help a lot. Here’s the highlights:

  • You can set environment variables in your provider by modifying its

    Info.plist
    . For example, adding an entry like this:
    <key>XPCService</key>
    <dict>
      <key>EnvironmentVariables</key>
      <dict>
          <key>FOO</key>
          <string>bar</string>
      </dict>
    </dict>

    will set the

    FOO
    environment variable to
    bar
    .
  • That opens up the possibility of using memory management features enabled by environment variables, for example, those documented in the

    malloc
    man page. Of those,
    MallocStackLoggingNoCompact
    is the heavy hitter.
  • Once you have

    MallocStackLoggingNoCompact
    set, Xcode’s memory graph feature becomes super useful. To wit:
    1. Start your provider in the normal way.

    2. Attach to it with Xcode (Debug > Attach to Process).

    3. In the debug pane, click the memory graph button.

    You can explore the memory graph interactively with Xcode, but you can also export it to disk (File > Export Memory Graph). The resulting

    .memgraph
    file can be passed to a variety of command-line tools for memory analysis, including
    heap
    ,
    leaks
    , and
    malloc_history
    (all of which have their own man page).

You can learn more about memory graphs in WWDC 2018 Session 416 iOS Memory Deep Dive.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Same In Xcode 10.2.

But I found another way to solve it,

1. Run app as normal.

2. Attach to packet tunnel process.

3. Under "Show the Debug navigator tab" select packet tunnel process, then click "Profile in instruments".

4. Maybe you'll be asked to attach to a process again when certain instrument app launched, select packet tunnel process you want to profile if been asked.

Trying this technique with Xcode 11 aand iOS 12 and don't see environment variable (and memory graph improvement) in packet tunnel.

https://imgur.com/sctDeAo

Still same In Xcode 12.4 (and probably forever).

But attaching seems work fine, if you know the trick:

  1. Call waitForDebugger(...) method (somewhere you're sure to be executed, by step #5), and place breakpoint after that (at least one).
  2. Run App normally (or with debugger).
  3. From within Xcode's "Debug" menu, click the "Attach to Process by PID or Name..." entry.
  4. In resulting dialog, write your extension-target's Name ONLY (instead of bundle-id), and click "Attach".
  5. Start your extension, so that it triggers step #1's logic (and later breakpoint).
  6. 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.
  7. 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 :-(

Add a Comment