2013-07-11 1 views
0

Привет, я пытаюсь протестировать код, чтобы узнать, предназначен ли он для решения проблем памяти.
К сожалению, я должен сделать это с помощью c-блока, который очень плохо справляется с сегментационными ошибками (просто выходит из строя)
Итак, мой вопрос: могу ли я добавить обработчик сигнала в код, который я тестирую, чтобы он мог выйти оскорбительной функции вместо выхода из всей программы?как выйти из функции, которая вызывает segfault и вернуться к вызывающей функции

По существу, можно ли изменить следующий обработчик для выхода из функции и возврата к моей структуре cunit?

void sighandler(int signo, siginfo_t *si, ucontext_t* context) 
{ 
    printf("Handler executed for signal %d\n", signo); 
    exit(0); /* can i replace this with exit from function? */ 
} 
+1

Вы, вероятно, не хотите ловить 'SIGSEGV' в первую очередь.Исправьте или отключите данный тест, пока он не будет исправлен. – devnull

+0

Фактически я вызываю функцию без инициализации памяти, чтобы проверить, проверяет ли она недопустимые входы, но функции seg_faults и аварийно завершает всю программу, которая включает в себя мою модульную систему тестирования. – Venkat

+0

Посмотрите на setjmp/longjmp. См. 'Man longjmp'. Вот подробности, связанные с их использованием в обработчиках сигналов: http://www.gnu.org/software/libc/manual/html_node/Longjmp-in-Handler.html –

ответ

1

Это может быть сделано с помощью обработчика сигнала плюс setjmp/longjmp.

Я не уверен, насколько это надежное решение. Он работает, когда я тестировал его, но может включать неопределенное поведение, поэтому не полагайтесь на него.

Также важно отметить, что багги-код может ухудшать ситуацию, чем ошибка сегментации. Это может быть, например, поврежденная память, поэтому ваш тестовый код или другой проверенный код не удастся.

Код в основном базируется на this answer.

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

static void good_func(void) 
{ 
    int q, *p = &q; 
    printf("%s running\n", __FUNCTION__); 
    *p = 3; 

} 
static void bad_func(void) 
{ 
    int *p = NULL; 
    printf("%s running\n", __FUNCTION__); 
    *p = 3; 
} 

static jmp_buf context; 

static void sig_handler(int signo) 
{ 
    longjmp(context, 1); 
} 

static void catch_segv(int catch) 
{ 
    struct sigaction sa; 

    if (catch) { 
     memset(&sa, 0, sizeof(struct sigaction)); 
     sa.sa_handler = sig_handler; 
     sa.sa_flags = SA_RESTART; 
     sigaction(SIGSEGV, &sa, NULL); 
    } else { 
     sigemptyset(&sa); 
     sigaddset(&sa, SIGSEGV); 
     sigprocmask(SIG_UNBLOCK, &sa, NULL); 
    } 
} 

typedef void (*func_t)(void); 

static int safe_run(func_t func) 
{ 
    catch_segv(1); 

    if (setjmp(context)) { 
      catch_segv(0); 
      return 0; 
    } 

    func(); 
    catch_segv(0); 
    return 1; 
} 

int main() 
{ 
    printf("good_func is %s\n", safe_run(good_func) ? "good" : "bad"); 
    printf("bad_func is %s\n", safe_run(bad_func) ? "good" : "bad"); 
    return 0; 
} 
+0

К сожалению, метод setjmp/longjmp очень ненадежный. Возникла проблема с setjmp, longjmp, которая может вызвать эту проблему, как описано в http://web.eecs.utk.edu/~huangj/cs360/360/notes/Setjmp/lecture.html ключевым моментом, являющимся вами НЕ МОЖЕТ ВОЗВРАТИТЬ ИЗ ПРОЦЕДУРЫ, КОТОРЫЙ ВЫЗЫВАЕТ setjmp() , который также может работать, вы не можете использовать setjmp вместе с вызовами функций. , поэтому может быть попытка восстановить стек уровня тестирования над тестируемой функцией, вызвав повторную попытку seg (бесконечный цикл). В настоящее время мы изучаем использование getcontext(), setcontext() вместо – Venkat

+0

@Venkat, здесь это не проблема. Я вызываю 'setjmp' из' safe_run' и не возвращаюсь от него до тех пор, пока все не будет выполнено. Это нормально, чтобы вернуться из процедуры, которая называется 'setjmp' - это просто означает, что вы больше не можете вызывать longjmp с сохраненным контекстом. – ugoren

+0

Хорошо, я проверил код, и он отлично работает. спасибо – Venkat

1

Вот аналогичный вопрос с ответом (хотя и не на том языке, C++): Catch Segfault or any other errors/exceptions/signals in C++ like catching exceptions in Java

Я считаю, что то, что вы ищете является исключением в месте вашего выхода (0) заявление , но ошибка Сегментации не является исключением, поэтому затрудняет ее устранение. При этом оказывается, что C не имеет встроенной обработки исключений.

Несмотря на это, вот справка о приемах в C++: http://www.cplusplus.com/doc/tutorial/exceptions/

Я не знаю, как именно вы используете функцию, которую вы здесь, но, возможно, вы можете вернуть булево вместо пустоты, и возвращения из вызывающая функция, если логическое значение истинно?

+0

На самом деле все, что я хочу сделать, это вызвать что-то, чтобы выйти из функции который вызывает seg_fault в обработчике сигналов, поэтому я могу продолжить тестирование, так как по существу мне нужно показать результаты, говорящие так, и поэтому входы вызывают seg_faults в этих 100 случаях и т. д. – Venkat

0

Если вы точно знаете, что в вашей функции ожидается SEGV, вы можете попробовать установить флаг в обработчик сигнала. (На самом деле, обратите внимание, что набор функций, которым вы можете звонить изнутри обработчика сигнала, крайне ограничен, поэтому установка флага - это практически все, что вы можете сделать в любом случае.)

Объявите переменную вашего флага как volatile int caused_segv = 0, позвольте своему обработчик сигнала caused_segv = 1, и в вашем коде после выполнения действия, которое может поднять SIGSEGV, проверьте, отличен ли caused_segv.

Кроме того, обратите внимание, что в POSIX.1, SIGFPE, SIGILL и SIGSEGV явно окликнул таким образом, что при завершении обработчика сигнала, поведение не определено. Для любого другого сигнала «программа должна возобновить исполнение в том месте, где оно было прервано».

Итак, ответ на ваш вопрос на самом деле: вы не можете надежно делать то, что вы пытаетесь сделать, потому что получение SIGSEGV автоматически оставляет вас в состоянии неопределенного поведения, но вы можете использовать то, что я дал вам выше, пытаться.