如何让lldb忽略EXC_BAD_ACCESS异常?

时间:2014-11-09 14:14:31

标签: macos signals lldb

我正在Mac OSX上编写一个程序,具体取决于sigaction / sa_handler机制。从用户运行代码片段,随时准备捕获信号/异常。该程序工作正常,但问题是我无法用lldb调试它。即使我设置了

,lldb似乎也无法忽略任何异常
proc hand -p true -s false SIGSEGV 
proc hand -p true -s false SIGBUS

控制流在触发异常的指令处停止,并且不会跳转到我之前安装的sa_handler,即使我尝试了命令c。输出是:

Process 764 stopped
* thread #2: tid = 0xf140, 0x00000001000b8000, stop reason = EXC_BAD_ACCESS (code=2, address=0x1000b8000)

如何让lldb忽略异常/信号并让程序的sa_handler工作呢?

编辑:示例代码

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>

static void handler(int signo, siginfo_t *sigaction, void *context)
{
    printf("in handler.\n");
    signal(signo, SIG_DFL);
}

static void gen_exception()
{
    printf("gen_exception in.\n");
    *(int *)0 = 0;
    printf("gen_exception out.\n");
}

void *gen_exception_thread(void *parg)
{
    gen_exception();
    return 0;
}

int main()
{
    struct sigaction sa;
    sa.sa_sigaction = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;

    if(sigaction(/*SIGBUS*/SIGSEGV, &sa, NULL) == -1) {
        printf("sigaction fails.\n");
        return 0;
    }

    pthread_t id;
    pthread_create(&id, NULL, gen_exception_thread, NULL);
    pthread_join(id, NULL);

    return 0;
}

4 个答案:

答案 0 :(得分:3)

这是Mac OS X中调试器界面中的一个长期存在的错误(gdb也有同样的问题...)如果您有开发人员帐户,请提交http://bugreport.apple.com的错误。因此很少有人真正使用SIGSEGV处理程序,这个问题永远不会引起内核人员的注意,所以更多的错误是好的......

答案 1 :(得分:2)

我在最近的一个项目中需要这个,所以我只是建立了自己的LLDB。我在tools/debugserver/source/MacOSX/MachTask.mm

修补了一行
err = ::task_set_exception_ports (task, m_exc_port_info.mask, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);

err = ::task_set_exception_ports (task, m_exc_port_info.mask & ~EXC_MASK_BAD_ACCESS, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);

导致调试服务器无法捕获EXC_BAD_ACCESS个异常。现在,我的自定义LLDB工作得很好:它仍然会捕获SIGSEGVSIGBUS,但在面对EXC_BAD_ACCESS时不再进入愚蠢的无限循环。在先前致命的信号上设置process handle选项也可以正常工作,我现在可以调试SEGV处理程序而不受惩罚。

Apple真的应该在LLDB中做出这个选项......对他们来说似乎是一个非常简单的解决方案。

答案 2 :(得分:0)

一些示例代码可以让这样的问题更容易回答......我之前从未使用过sigaction API,但我将它们放在一起 -

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void segv_handler (int in)
{
    puts ("in segv_handler()");
}

void sigbus_handler (int in)
{
    puts ("in sigbus_handler()");
}

int main ()
{
    struct sigaction action;
    action.sa_mask = 0;
    action.sa_flags = 0;


    action.sa_handler = segv_handler;
    sigaction (SIGSEGV, &action, NULL);
    action.sa_handler = sigbus_handler;
    sigaction (SIGBUS, &action, NULL);

    puts ("about to send SIGSEGV signal from main()");
    kill (getpid(), SIGSEGV);

    puts ("about to send SIGBUS signal from main()");
    kill (getpid(), SIGBUS);

    puts ("exiting main()");

}


% lldb a.out
(lldb) br s -n main
(lldb) r
(lldb) pr h -p true -s false SIGSEGV SIGBUS
(lldb) c
Process 54743 resuming
about to send SIGSEGV signal from main()
Process 54743 stopped and restarted: thread 1 received signal: SIGSEGV
in segv_handler()
about to send SIGBUS signal from main()
Process 54743 stopped and restarted: thread 1 received signal: SIGBUS
in sigbus_handler()
exiting main()
Process 54743 exited with status = 0 (0x00000000) 
(lldb) 

一切看起来都像是在这里正常工作。如果我将-n false添加到process handle参数中,则lldb不会打印关于Process .. stopped and restarted的行。

请注意,这些信号设置不会在流程执行过程中持续存在。因此,如果您已经开始调试会话(r一旦您已启动该流程一次),那么您需要重新设置这些会话。您可能需要创建命令别名快捷方式并将其放在~/.lldbinit文件中,以便您可以使用短cmd设置进程处理方式。

答案 3 :(得分:0)

我们可以轻松做到。只需添加此代码即可。

#include <mach/task.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>

int ret = task_set_exception_ports(
                                   mach_task_self(),
                                   EXC_MASK_BAD_ACCESS,
                                   MACH_PORT_NULL,//m_exception_port,
                                   EXCEPTION_DEFAULT,
                                   0);

别忘了这样做

proc hand -p true -s false SIGSEGV 
proc hand -p true -s false SIGBUS

enter image description here

完整代码:

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>

#include <mach/task.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>

static void handler(int signo, siginfo_t *sigaction, void *context)
{
    printf("in handler.\n");
    signal(signo, SIG_DFL);
}

static void gen_exception()
{
    printf("gen_exception in.\n");
    *(int *)0 = 0;
    printf("gen_exception out.\n");
}

void *gen_exception_thread(void *parg)
{
    gen_exception();
    return 0;
}

int main()
{
    task_set_exception_ports(
                             mach_task_self(),
                             EXC_MASK_BAD_ACCESS,
                             MACH_PORT_NULL,//m_exception_port,
                             EXCEPTION_DEFAULT,
                             0);
    
    
    struct sigaction sa;
    sa.sa_sigaction = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;

    if(sigaction(/*SIGBUS*/SIGSEGV, &sa, NULL) == -1) {
        printf("sigaction fails.\n");
        return 0;
    }

    pthread_t id;
    pthread_create(&id, NULL, gen_exception_thread, NULL);
    pthread_join(id, NULL);

    return 0;
}

请参阅(中文文章):https://zhuanlan.zhihu.com/p/33542591