55

I set up my application to either send debugging output to console or a log file. Now, I'd like to decide with in the code whether

  • it is run in the debugger (or simulator) and have thus a console window where I would like to read the output directly or if
  • there is no console window and thus, the output should be redirected to a file.

Is there a way to determine if the app runs in the debugger?

Axel
  • 1,716
  • 1
  • 16
  • 28
  • Check out the following answer http://stackoverflow.com/questions/458304/how-can-i-programmatically-determine-if-my-app-is-running-in-the-iphone-simulator – fsaint Jan 20 '11 at 08:29
  • 2
    @Felz (@angrest): A program can be run on the device and still in the debugger. – kennytm Jan 20 '11 at 08:40
  • @KennyTM is totally right. Debug in the device and you will get a console. – fsaint Jan 20 '11 at 08:53

8 Answers8

50

There's a function from Apple to detect whether a program is being debugged in the Technical Q&A 1361 (entry in Mac library and entry in iOS library; they are identical).

Code from the Technical Q&A:

#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 );
}

Also pay attention to this note at the end of the Q&A:

Important: Because the definition of the kinfo_proc structure (in <sys/sysctl.h>) is conditionalized by __APPLE_API_UNSTABLE, you should restrict use of the above code to the debug build of your program.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • 7
    There's one thing that I don't understand in that document... After the code it says: "Because the definition of the kinfo_proc structure (in ) is conditionalized by __APPLE_API_UNSTABLE, you should restrict use of the above code to the debug build of your program.". So... what's the point of the code if I can include it only in the debug build? If I can use it only in the debug build... it means that I know already that I'm in the debug build... – Andrea Sep 13 '12 at 23:29
  • 5
    The question was not about a whether a _build_ is a _debug build_ but whether the app is _running in a debugger_. The question already assumes that a debug build is made and run and then just wants to decide what to do with the log messages. You're right that document in effect says you should not use this technique in a release build that you ship to customers. – DarkDust Sep 14 '12 at 09:18
  • thanks for the clarification. I was hoping to use your suggestion to solve a similar issue that I'm having (http://stackoverflow.com/questions/12406831/check-for-release-build-at-runtime). Unfortunately it doesn't look like the right one then... – Andrea Sep 14 '12 at 16:44
  • 1
    Looks like kinfo_proc is not conditionalized by __APPLE_API_UNSTABLE anymore, so I hope it's fine to use it not only in debug – silyevsk Jun 28 '16 at 09:26
  • any idea of how can i implement it to swift app delegate? – Konstantinos Natsios Jan 18 '18 at 12:42
18

It is possible to instruct the debugger to set environment variables when it launches a process it is about to debug. This can be done in Xcode by going to the menu item Product->Edit Scheme. Then under the Debug scheme's Arguments tab add a new environment variable. The variable should be named "debugger" with the value "true". Then the following code snippet can be used to determine if the debugger launched your process:

NSDictionary* env = [NSProcessInfo processInfo].environment;

if ([env[@"debugger"] isEqual:@"true"]) {
    NSLog(@"debugger yes");
}
else {
    NSLog(@"debugger no");
}
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Evan
  • 6,151
  • 1
  • 26
  • 43
  • Well, that does not seem to be a solution - when the app runs within the iPhone attached to XCode/Debugger - how should I check an environment variable of the machine XCode runs on? – Axel Jan 20 '11 at 09:03
  • 1
    The debugger sets up the environment of the process it launches. The possibilities either on the simulator or on a device. In both cases the debugger can add environment variables. I tested it on a device and simulator and it worked. The environment variables are not being set on the host machine but for the application running on iOS. – Evan Jan 20 '11 at 09:28
  • @Evan Even though this worked in the past it doesn't work at least in xcode 4.5. There is no such environment variable. – Tertium Oct 08 '12 at 15:34
  • 1
    @Tertium I have update the answer to point you at the right spot in Xcode to set an environment variable. – Evan Oct 08 '12 at 20:35
  • Thanks, I'll give it a try tomorrow and write back. – Tertium Oct 08 '12 at 21:34
17

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

func isDebuggerAttached() -> Bool {
    return getppid() != 1
}
Jochen Holzer
  • 1,598
  • 19
  • 25
10

The simplest solution actually is

_isDebugging = isatty(STDERR_FILENO);

It isn't exactly the same as telling whether the app is running under debugger, but good enough (even better?) to determine whether the log should be written to disk.

Siyuan Ren
  • 7,573
  • 6
  • 47
  • 61
7

Based off an answer in a duplicate thread that was for Objective-C as well and showed how HockeyApp-iOS does it, here's a Swift 5 version:

let isDebuggerAttached: Bool = {
    var debuggerIsAttached = false

    var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    var info: kinfo_proc = kinfo_proc()
    var info_size = MemoryLayout<kinfo_proc>.size

    let success = name.withUnsafeMutableBytes { (nameBytePtr: UnsafeMutableRawBufferPointer) -> Bool in
        guard let nameBytesBlindMemory = nameBytePtr.bindMemory(to: Int32.self).baseAddress else { return false }
        return -1 != sysctl(nameBytesBlindMemory, 4, &info/*UnsafeMutableRawPointer!*/, &info_size/*UnsafeMutablePointer<Int>!*/, nil, 0)
    }

    // The original HockeyApp code checks for this; you could just as well remove these lines:
    if !success {
        debuggerIsAttached = false
    }

    if !debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0 {
        debuggerIsAttached = true
    }

    return debuggerIsAttached
}()
ctietze
  • 2,805
  • 25
  • 46
6

Always good to have different solutions, so here are my two cents:

The idea is to check the stderr filehandle (this is where NSLog prints to). This solution has reliably been working since at least iOS 4 and keeps doing so in iOS 9, both on the simulator and device.

#import <sys/ioctl.h>
#import <sys/param.h>
#if TARGET_IPHONE_SIMULATOR
    #import <sys/conf.h>
#else
// Not sure why <sys/conf.h> is missing on the iPhoneOS.platform.
// It's there on iPhoneSimulator.platform, though. We need it for D_DISK, only:
    #if ! defined(D_DISK)
        #define D_DISK  2
    #endif
#endif

BOOL isDebuggerAttatchedToConsole(void)
{
    // We use the type of the stderr file descriptor
    // to guess if a debugger is attached.

    int fd = STDERR_FILENO;

    // is the file handle open?
    if (fcntl(fd, F_GETFD, 0) < 0) {
        return NO;
    }

    // get the path of stderr's file handle
    char buf[MAXPATHLEN + 1];
    if (fcntl(fd, F_GETPATH, buf ) >= 0) {
        if (strcmp(buf, "/dev/null") == 0)
            return NO;
        if (strncmp(buf, "/dev/tty", 8) == 0)
            return YES;
    }

    // On the device, without attached Xcode, the type is D_DISK (otherwise it's D_TTY)
    int type;
    if (ioctl(fd, FIODTYPE, &type) < 0) {
        return NO;
    }

    return type != D_DISK;
}
Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
-1

I usually go for a much more simple solution; is the binary compiled with optimizations?

A debug build is not optimized, and logs are nice. A release build should have optimizations and not as many logs. You can check for this with the __OPTIMIZE__ symbol.

For logging I use this setup for logg-functions:

#ifdef __OPTIMIZE__ 
  #define CWLog(...)
  #define CWLogDebug(...)
  #define CWLogInfo(...)
#else
  #define CWLog(...) NSLog(__VA_ARGS__)
  #define CWLogDebug( s, ... ) NSLog( @"DEBUG <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
  #ifndef LOG_INFO
    #define CWLogInfo(...)
  #else
    #define CWLogInfo( s, ... ) NSLog( @"INFO <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
  #endif
#endif
#define CWLogWarning( s, ... ) NSLog( @"WARNING <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#define CWLogError( s, ... ) NSLog( @"ERROR <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
PeyloW
  • 36,742
  • 12
  • 80
  • 99
  • 3
    Hint: Debug builds sometimes are optimised. You can change the build setting for it. As long as the developer doesn't change it from the default, your answer is valid. ;-) – Constantino Tsarouhas Dec 31 '11 at 14:19
  • 11
    I'm afraid that this answer misses the point of the question. The poster wanted to output the debug messages either to the console or a file, depending on whether the app is run in a debugger or not, not whether it is a debug _build_ or not. – DarkDust Sep 14 '12 at 09:22
-9

Why not using conditional compilation block in Swift?

     #if DEBUG
        // Do something.
     #endif

Any objection?

You can define if you want a runtime constant

#if DEBUG
public let IS_RUNNING_IN_DEBUGGER: Bool = true
#else
public let IS_RUNNING_IN_DEBUGGER: Bool = false
#endif

The same approach can be used in Objc & more.

bpds
  • 676
  • 7
  • 12
  • Oddly though when I launch my DEBUG-built executable directly on the device without starting from Xcode, it still returns true... – Dylan Nicholson Oct 17 '18 at 02:37