检测iOS应用程序是否在调试器中运行

时间:2011-01-20 08:20:32

标签: iphone objective-c debugging ios

我将应用程序设置为将调试输出发送到控制台或日志文件。现在,我想在代码中决定是否

  • 它在调试器(或模拟器)中运行,因此有一个控制台窗口,我想直接读取输出或
  • 没有控制台窗口,因此输出应重定向到文件。

有没有办法确定应用程序是否在调试器中运行?

8 个答案:

答案 0 :(得分:39)

Apple提供了一项功能,用于检测是否正在 Technical Q& A 1361 entry in Mac libraryentry in iOS library中调试程序;它们是相同的)。

技术问答的代码:

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

在Q&amp; A:

的末尾也要注意这个注释
  

重要提示:由于kinfo_proc结构的定义(<sys/sysctl.h>)由__APPLE_API_UNSTABLE条件化,因此您应限制使用上述代码调试程序的构建。

答案 1 :(得分:15)

可以指示调试器在启动要调试的进程时设置环境变量。这可以通过转到菜单项Product-&gt; Edit Scheme在Xcode中完成。然后在Debug方案的Arguments选项卡下添加一个新的环境变量。该变量应命名为“debugger”,值为“true”。然后,可以使用以下代码段来确定调试器是否启动了您的进程:

NSDictionary* env = [NSProcessInfo processInfo].environment;

if ([env[@"debugger"] isEqual:@"true"]) {
    NSLog(@"debugger yes");
}
else {
    NSLog(@"debugger no");
}

答案 2 :(得分:6)

总是很高兴有不同的解决方案,所以这是我的两分钱:

这个想法是检查stderr文件句柄(这是NSLog打印到的地方)。至少从iOS 4开始,这个解决方案已经可靠地运行了,并且在iOS 9中一直在模拟器和设备上都这样做。

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

答案 3 :(得分:4)

最简单的解决方案实际上是

_isDebugging = isatty(STDERR_FILENO);

它与确定应用程序是否在调试器下运行完全相同,但是足够好(甚至更好?)来确定是否应该将日志写入磁盘。

答案 4 :(得分:4)

对我来说,这段快速代码完美无缺:

func isDebuggerAttached() -> Bool {
    return getppid() != 1
}

答案 5 :(得分:2)

我通常会寻求更简单的解决方案;是用优化编译的二进制文件吗?

调试版本未优化,日志也很好。发布版本应该具有优化而不是多个日志。您可以使用__OPTIMIZE__符号检查此内容。

对于日志记录,我将此设置用于logg- 功能

#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__] )

答案 6 :(得分:1)

同样基于Objective-C的an answer in a duplicate thread,并展示了HockeyApp-iOS是如何实现的,这是一个Swift 5版本:

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
}()

答案 7 :(得分:-4)

为什么不在Swift中使用条件编译块?

     #if DEBUG
        // Do something.
     #endif

有异议吗?

您可以定义是否需要运行时常量

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

Objc等中可以使用相同的方法。