Я пишу программу на 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;
}
8
jay 9 Ноя 2014 в 17:14

4 ответа

Лучший ответ

Мне это было нужно в недавнем проекте, поэтому я только что построил свой собственный 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 работает нормально: он по-прежнему улавливает SIGSEGV и SIGBUS, но больше не входит в глупый бесконечный цикл при столкновении с EXC_BAD_ACCESS. Установка параметров process handle для ранее фатальных сигналов тоже работает нормально, и теперь я могу безнаказанно отлаживать обработчики SEGV.

Apple действительно должна сделать эту опцию в LLDB ... кажется, для них это действительно простое решение.

5
nneonneo 22 Сен 2015 в 18:16

Это давняя ошибка в интерфейсе отладчика в Mac OS X (у gdb была такая же проблема ...). Если у вас есть учетная запись разработчика, сообщите об ошибке с помощью http://bugreport.apple.com. Так мало людей на самом деле используют обработчики SIGSEGV, что проблема никогда не привлекает внимания со стороны разработчиков ядра, поэтому больше ошибок - это хорошо ...

5
Jim Ingham 10 Ноя 2014 в 22:11

Мы легко справимся. Просто добавьте этот код.

#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

1
Yanni 21 Сен 2020 в 03:30

Небольшой пример кода может упростить ответ на такой вопрос ... Я никогда раньше не использовал 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.

0
Jason Molenda 10 Ноя 2014 в 03:48