Обнаружение запуска приложения iOS в отладчике

Я настроил свое приложение на отправку вывода отладки на консоль или в файл журнала. Теперь я хотел бы решить с помощью кода

  • он запускается в отладчике (или симуляторе) и, таким образом, имеет консольное окно, где я хотел бы прочитать вывод напрямую или, если
  • нет консольного окна и, следовательно, вывод должен быть перенаправлен в файл.

Есть ли способ определить, работает ли приложение в отладчике?

7 ответов

Решение

В Apple есть функция, позволяющая определить, выполняется ли отладка программы в Технических вопросах и ответах 1361 ( запись в библиотеке Mac и запись в библиотеке iOS; они идентичны).

Код из Технических вопросов и ответов:

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

Также обратите внимание на это примечание в конце вопросов и ответов:

Важно: потому что определение kinfo_proc структура (в <sys/sysctl.h>) обусловлен __APPLE_API_UNSTABLE, вы должны ограничить использование приведенного выше кода отладочной сборкой вашей программы.

Можно дать отладчику команду установить переменные среды при запуске процесса, который он собирается отлаживать. Это можно сделать в Xcode, перейдя в пункт меню Product->Edit Scheme. Затем на вкладке Аргументы схемы отладки добавьте новую переменную среды. Переменная должна иметь имя "отладчик" со значением "true". Затем можно использовать следующий фрагмент кода, чтобы определить, запустил ли отладчик ваш процесс:

NSDictionary* env = [NSProcessInfo processInfo].environment;

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

Для меня этот кусок swift-кода работает отлично:

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

Самое простое решение на самом деле

_isDebugging = isatty(STDERR_FILENO);

Это не то же самое, что сказать, работает ли приложение под отладчиком, но достаточно хорошо (даже лучше?), Чтобы определить, следует ли записывать журнал на диск.

Основываясь на ответе в дублированном потоке, который также был для Objective-C и показал, как это делает 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
}()

Всегда хорошо иметь разные решения, поэтому вот мои два цента:

Идея состоит в том, чтобы проверить файловый дескриптор 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;
}

Я обычно иду к гораздо более простому решению; скомпилирован бинарный файл с оптимизацией?

Отладочная сборка не оптимизирована, и журналы хороши. Сборка релиза должна иметь оптимизацию, а не столько журналов. Вы можете проверить это с помощью __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__] )

Почему бы не использовать блок условной компиляции в 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 & more.

Другие вопросы по тегам