我可以让iOS进程等待调试器吗?

时间:2018-07-31 16:33:39

标签: ios xcode lldb

在测试我的iOS应用程序时,我的代码有时会遇到意外情况(轻度断言失败),这会向我显示警报,让我知道发生问题的文件和行。

但是,到目前为止,我已经超越了“断言”失败的实际点,因此,即使我将LLDB附加到该过程中,也没什么要注意的。

是否有一种方法可以暂停iOS进程,直到连接调试器为止,这样LLDB将在故障发生时立即运行,可以访问调用堆栈,变量等?我不希望该应用程序崩溃,就像使用简单的断言那样。

2 个答案:

答案 0 :(得分:1)

通常这很容易-在程序中添加一个busyloop。用C术语来说,您可能有一个静态文件,例如

static int wait_for_debugger = 1;

,然后在您有了软断言的时候,

if (bad_condition)
{
    while (wait_for_debugger)
        sleep (1);
}

当您在应用程序中遇到这种情况时,线程将在此刻永远等待,直到您连接调试器并执行类似的操作

(lldb) e wait_for_debugger = 0

我看到的一个iOS应用程序的复杂之处在于,如果您的应用程序停止响应事件,SpringBoard将注意到并认为您的应用程序已挂起并杀死了它。如果您在主线程上的事件循环仍在执行,则可以,但是将另一个线程置于该busyloop中可能会导致其他问题。当调试器附加到iOS进程时,SpringBoard知道该应用程序停止响应事件是可以的(例如,您可能会在断点处停止),但是如果它免费运行,则我看不到该工作

另一种选择是将调试器一直附加到应用程序,直到重现故障为止-但我假设它只是偶尔发生?因此在每次回购失败之前,从Xcode启动它(或从Xcode附加到它)可能是额外的工作。另一方面,如果可以使用“始终在调试器下运行”模型,则可以在软断言上使用断点。

答案 1 :(得分:1)

这不是实际答案。我很抱歉,但是这次探索太有趣了,无法分享,时间太长,无法发表评论,我希望看到有人以此为基础。

我首先将应用程序设置为要调试(PT_TRACE_ME),然后触发断点(SIGTRAP)。只需从Swift中的IBAction按钮调用即可。

#include "debug.h"
#import <dlfcn.h>
#import <sys/types.h>
#include <signal.h>

typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);

void debugme() {
    void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(0, 0, 0, 0);
    dlclose(handle);
    kill(0, SIGTRAP);
}

(我确信这将禁止您进入应用商店。我永远不会在运输应用的生产版本中加入它。)

如此接近工作(如果您已经在Xcode下运行,则发送SIGTRAP确实可以工作,但是在这种情况下,您可以设置一个断点)。它挂起该程序,该程序不会因无法处理其运行循环而被杀死,它可能会出现在“调试,附加到过程”列表中。但是,当您尝试实际连接Xcode时,会抱怨它无法与其连接。在iOS日志中,您将获得:

default 16:10:52.889240 -0400   debugserver error: Attach failed: "(os/kern) invalid task".

这可能并不奇怪,因为该应用程序的父级并不希望对其进行调试。所以也许这表明这确实是不可能的。

所以,好的,它失败了。但这有点奇怪:

该应用程序不会死。我的意思是强行杀死不会杀死该应用程序。它仍然显示在Xcode的进程列表中。主页按钮将带您进入跳板,如果重新启动该应用程序,您只会得到一个白色屏幕(我猜是因为它甚至无法自我重绘?)

当应用程序处于前台状态时屏幕锁定时,主页按钮不再起作用以将其唤醒。 power 按钮不再起作用。保持关机状态最终会产生触觉脉冲,但并未关闭电话。我必须进行一次重置(电源+入户)才能使其重新启动。令我惊讶的是,我可以通过沙盒化的userland流程如此艰难地锁定手机。

所以也许这不是最好的方法,但是感觉非常接近实际答案,我还是想与大家分享。