Программа на C ++ не обрабатывает ни вызов функции, ни printf во время SIGSEGV в gcc - PullRequest
1 голос
/ 22 марта 2011

У меня проблема с получением вывода трассировки стека в stderr или с дампом в файл журнала. Я запускаю код в Kubuntu10.04 с компилятором gcc (4.4.3). Проблема в том, что в обычном режиме работы (без GDB) программа не выводит ничего, кроме «Ошибка сегментации». Я хочу вывести вывод обратной трассировки, как в приведенных ниже инструкциях печати. Когда я запускаю gdb с моим приложением, оно обращается к оператору printf / fprintf / (вызов функции), а затем выдает сбой со следующим оператором:

669     {
(gdb) 
670       printf("Testing for stability.\n");
(gdb) 

Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff68b1f45 in puts () from /lib/libc.so.6

Странно то, что это работает, если я вызываю функцию в том же файле, который дает сбой, работает нормально и выдает правильные результаты. Но если программа выходит из строя в функции вне этого файла, она не печатает никаких выходных данных. Таким образом, оператор printf, дамп файла или вызов функции не обрабатываются. Я использую следующий пример кода:

void bt_sighandler(int sig, siginfo_t *info,
               void *secret) {

void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t *)secret;

/* Do something useful with siginfo_t */
if (sig == SIGSEGV)
  printf("Got signal %d, faulty address is %p, "
       "from %p\n", sig, info->si_addr, 
       uc->uc_mcontext.gregs[0]);
else
  printf("Got signal %d#92; \n", sig);

trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
trace[1] = (void *) uc->uc_mcontext.gregs[0];

messages = backtrace_symbols(trace, trace_size);
/* skip first stack frame (points here) */
printf("[bt] Execution path:#92; \n");
for (i=1; i<trace_size; ++i)
  printf("[bt] %s#92; \n", messages[i]);

exit(0);
}


int main() {

/* Install our signal handler */
struct sigaction sa;

sa.sa_sigaction = (void *)bt_sighandler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;

sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
/* Do something */
printf("%d#92; \n", func_b());
}

Заранее спасибо за любую помощь.

Ответы [ 6 ]

3 голосов
/ 22 марта 2011

К сожалению, в обработчике SIGSEGV просто невозможно что-либо сделать надежно. Подумайте об этом так: ваша программа имеет серьезную ошибку, и ее состояние (включая состояние системного уровня, например, кучу) находится в несогласованном состоянии.

В таком случае вы не можете ожидать, что ОС волшебным образом исправит кучу и другие внутренние компоненты, необходимые для выполнения произвольного кода в вашем обработчике сигналов.

Если SEGV происходит в вашем собственном коде, хорошим решением будет использование ядра и устранение проблемы с корнем. Если ядро ​​происходит в другом коде, скажем, через общую библиотеку, я бы предложил выделить этот код в совершенно отдельный двоичный файл и обмениваться данными между двумя двоичными файлами. Затем, если происходит сбой библиотеки, ваша основная программа не работает.

2 голосов
/ 22 марта 2011

Предполагается, что в обработчике сигналов вы делаете очень мало, в принципе обращаетесь только к переменным типа sig_atomic_t и volatile data.

Ввод / вывод определенно исключен.См. Эту страницу для GCC:

http://www.gnu.org/s/libc/manual/html_node/Nonreentrancy.html#Nonreentrancy

0 голосов
/ 25 марта 2011

Мне удалось заставить его работать частично. На самом деле я запускал приложение в режиме sudo. Запуск его в режиме пользователя дает мне стек вызовов. Однако работа в пользовательском режиме отключает аппаратное ускорение (графические драйверы nvidia). Чтобы решить эту проблему, я добавил себя в группу «видео», чтобы у меня был доступ к / dev / nvidia0 & / dev / nvidiactl. Однако, когда я получаю доступ, стек больше не генерируется. Только когда я в пользовательском режиме и аппаратное ускорение отключено, стек идет. Но я не могу запустить свое приложение без аппаратного ускорения (что означает, что некоторые важные функции будут отключены). Пожалуйста, дайте мне знать, если у кого-нибудь есть идеи.

Спасибо.

0 голосов
/ 22 марта 2011

Когда происходит сбой приложения, Linux создает дамп ядра с указанием состояния приложения в момент сбоя. Основной файл может быть проверен с помощью gdb.

Если файл ядра не создан, попробуйте изменить размер файла ядра с помощью

ulimit -c unlimited

в той же оболочке и до запуска программы. Имя основного файла обычно - core.PID, где PID - это pid программы. Файл ядра обычно находится где-то в / tmp или в каталоге, где была запущена программа.

Гораздо больше информации о файлах ядра доступно на странице руководства для ядра. Используйте

man core

чтобы прочитать справочную страницу.

0 голосов
/ 22 марта 2011

Есть ли причина, по которой вы не можете использовать valgrind?

0 голосов
/ 22 марта 2011

Попробуйте использовать более простые функции, такие как strcat () и write ().

...