30

I would like to programmatically determine if the iOS app is being run directly from XCode (either in the simulator or on a tethered device). I've tried the -D DEBUG solution described here, but when I then disconnect from Xcode and re-run the app, it still thinks it's in debug mode. I think what I'm looking for is a Swift version of this function

#include <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>

static bool AmIBeingDebugged(void)
    // Returns true if the current process is being debugged (either 
    // running under the debugger or has a debugger attached post facto).
{
    int                 junk;
    int                 mib[4];
    struct kinfo_proc   info;
    size_t              size;

    // Initialize the flags so that, if sysctl fails for some bizarre 
    // reason, we get a predictable result.

    info.kp_proc.p_flag = 0;

    // Initialize mib, which tells sysctl the info we want, in this case
    // we're looking for information about a specific process ID.

    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();

    // Call sysctl.
    size = sizeof(info);
    junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
    assert(junk == 0);

    // We're being debugged if the P_TRACED flag is set.
    return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
}
Community
  • 1
  • 1
Mike Hedman
  • 1,391
  • 1
  • 11
  • 30
  • 1
    You can call C functions from Swift, so you don't really have to translate it. – Martin R Oct 16 '15 at 18:24
  • Martin, that is the answer I was going to put, place it as an answer and I will upvote it. – Knight0fDragon Oct 16 '15 at 18:29
  • Here's a quick, untested attempt at converting the code to Swift (just for fun): https://gist.github.com/getaaron/8d48489274a873835636. I don't have time to play with it further right now, but maybe it will get you started. – Aaron Brager Oct 16 '15 at 18:47
  • @Knight0fDragon: Translating it to Swift is the more interesting challenge :) – Martin R Oct 16 '15 at 18:59

2 Answers2

66

Clarification: Your C code (and the Swift version below) checks if the program is run under debugger control, not if it's being run from Xcode. One can debug a program outside of Xcode (by calling lldb or gdb directly) and one can run a program from Xcode without debugging it (if the “Debug Executable” checkbox in the scheme setting is off).


You could simply keep the C function and call it from Swift. The recipes given in How do I call Objective-C code from Swift? apply to pure C code as well.

But it is actually not too complicated to translate that code to Swift:

func amIBeingDebugged() -> Bool {
    // Buffer for "sysctl(...)" call's result.
    var info = kinfo_proc()
    // Counts buffer's size in bytes (like C/C++'s `sizeof`).
    var size = MemoryLayout.stride(ofValue: info)
    // Tells we want info about own process.
    var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    // Call the API (and assert success).
    let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
    assert(junk == 0, "sysctl failed")
    // Finally, checks if debugger's flag is present yet.
    return (info.kp_proc.p_flag & P_TRACED) != 0
}

Update for Swift 5 (Xcode 10.7): strideofValue and the related functions do not exist anymore, they have been replaced by MemoryLayout.stride(ofValue:).

Remarks:

  • kinfo_proc() creates a fully initialized structure with all fields set to zero, therefore setting info.kp_proc.p_flag = 0 is not necessary.
  • The C int type is Int32 is Swift.
  • sizeof(info) from the C code has to be strideOfValue(info) in Swift to include the structure padding. With sizeofValue(info) the above code always returned false in the Simulator for 64-bit devices. This was the most difficult part to figure out.

Swift 2 logic:

func amIBeingDebugged() -> Bool {
    var info = kinfo_proc()
    var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    var size = strideofValue(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
}
Top-Master
  • 7,611
  • 5
  • 39
  • 71
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 3
    Nice find with `strideOfValue`. I didn't know about the difference. – Aaron Brager Oct 17 '15 at 15:59
  • Hey Martin, can you provide an Obj-C version of your Xcode 8 solution to detect the debugger? The provided one from Apple does not seem to work properly anymore. Thank you! :-) – Hans Knöchel Sep 09 '16 at 20:41
  • @HansKnoechel: The code in the question is pure C and can be used from Objective-C without problems. How does it not "work properly"? – Martin R Sep 09 '16 at 21:12
0

For those looking for a simpler solution - this works perfectly:

func isDebuggerAttached() -> Bool {
    return getppid() != 1
}
Jochen Holzer
  • 1,598
  • 19
  • 25
  • I don't think so. You can attach a debugger to an Application started from the Finder (in which case the parent id is one), and you can start an application from the command line without debugger (in which case the parend id is different from one). – Martin R Jan 23 '22 at 20:32