在测试我的iOS应用程序时,我的代码有时会遇到意外情况(轻度断言失败),这会向我显示警报,让我知道发生问题的文件和行。
但是,到目前为止,我已经超越了“断言”失败的实际点,因此,即使我将LLDB附加到该过程中,也没什么要注意的。
是否有一种方法可以暂停iOS进程,直到连接调试器为止,这样LLDB将在故障发生时立即运行,可以访问调用堆栈,变量等?我不希望该应用程序崩溃,就像使用简单的断言那样。
答案 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流程如此艰难地锁定手机。
所以也许这不是最好的方法,但是感觉非常接近实际答案,我还是想与大家分享。