Почему обратная трассировка GDB ограничена при использовании управления с плавающей запятой? - PullRequest
0 голосов
/ 24 сентября 2019

Следуя совету этого вопроса , я добавил #include <fenv.h> и feenableexcept(FE_ALL_EXCEPT & ~FE_INEXACT); к своему основному источнику и скомпилировал его, используя g++ -O0 -Wall -Wextra -Werror -g main.cpp -o main.o.Все файлы в проекте скомпилированы таким образом, используя make.feenableexcept был добавлен только к основному.Вещи связаны между собой одинаковыми -O0 и -g.Затем я запускаю исполняемый файл как gdb a.out.Отладчик дает мне ожидаемое SIGFPE, однако обратная трассировка не дает обычно полезной информации.Вместо этого я получаю листинг, подобный этому:

Program received signal SIGFPE, Arithmetic exception.
0x00002aaaabe19c0d in _amd_handle_error () from /lib64/libm.so.6
(gdb) bt                                                        
#0  0x00002aaaabe19c0d in _amd_handle_error () from /lib64/libm.so.6
#1  0x00002aaaabe19cca in _pow_special () from /lib64/libm.so.6     
#2  0x00002aaaabdf6c12 in pow () from /lib64/libm.so.6              
#3  0xbfc971b779361ea1 in ?? ()                                     
#4  0x3fe85bf701f137d7 in ?? ()                                     
#5  0x3fe914100cd71275 in ?? ()                                     
#6  0x00007fffffffa420 in ?? ()                                     
#7  0x00007fffffff9960 in ?? ()                                     
#8  0x3fe7c514ca0e522d in ?? ()                                     
#9  0x0000000000000000 in ?? () 

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

Обычно, когда я использую -O0 и -g, я получаю функции, файлы и номера строк, связанные с трассировкой.Если я устанавливаю точку останова, используя тот же исполняемый файл и обратную трассировку от точки останова, я получаю полезную информацию.

Breakpoint 1, SurfaceModel::ProcessGroups (this=0x7fffffff9f30) at source/SurfaceModel.cpp:398
398             vector<Group>::iterator it;
(gdb) bt
#0  SurfaceModel::ProcessGroups (this=0x7fffffff9f30) at source/SurfaceModel.cpp:398
#1  0x00000000006768e6 in MainLoop (logFile=...) at source/main.cpp:94
#2  0x0000000000676337 in main (argc=1, argv=0x7fffffffbe18) at source/main.cpp:41

Затем я добавляю *(int*)0=0; к коду, чтобы вызвать ошибку сегмента, чтобы увидеть, было ли это связано ссигналы.Я также получил полезную информацию.

Program received signal SIGSEGV, Segmentation fault.
0x000000000061f42a in SurfaceModel::ClearGroupHeatRates (this=0x7fffffff9f30) at source/SurfaceModel.cpp:456
456             *(int*)0=0; // force a seg fault
(gdb) bt
#0  0x000000000061f42a in SurfaceModel::ClearGroupHeatRates (this=0x7fffffff9f30) at source/SurfaceModel.cpp:456
#1  0x0000000000676ba5 in MainLoop (logFile=...) at source/pilager.cpp:137
#2  0x0000000000676341 in main (argc=1, argv=0x7fffffffbe18) at source/pilager.cpp:41

Это, похоже, связано только с тем, что я делал с управлением с плавающей запятой.Я использую GDB 7.12, и это было скомпилировано с GCC 5.3.0.Есть ли способ сохранить информацию трассировки с помощью SIGFPE?

1 Ответ

0 голосов
/ 24 сентября 2019

Есть ли способ сохранить информацию о трассировке с помощью SIGFPE?

Информация о трассировке не имеет ничего общего с тем, какой сигнал поднимается, и не имеет ничего общего с функциейон поднимается в.

Каким-то образом в вашем pow отсутствует дескриптор размотки (именно это GDB использует для размотки стека).

Это часто происходит с реализациями на уровне сборки (где разработчикигнорирует вставку соответствующих директив .cfi) или при сборке кода с испорченными компиляторами.

Сломанный компилятор кажется маловероятным, и я не могу найти какие-либо последние версии GLIBC, которые использовали сборку для реализации pow.

Для восстановления стека могут работать следующие методы:

  1. Использовать обратный отладчик (например, rr ) и переходить назад от SIGFPE,Это лучшее решение, но я сомневаюсь, что rr доступно для вашей (очевидно, довольно старой) системы.
  2. Подсчитайте, сколько раз pow вызывается до сбоя:

    (gdb) break pow (gdb) commands 1 silent cont end (gdb) run # run until SIGFPE (gdb) info break
    Теперь вы будете знать, сколько раз pow вызывалось до сбоя.

    Запустите программу еще раз, игнорируя точку останова $N-1 раз (вам нужно будет удалить командысначала с точки останова и используйте команду GDB ignore 1 $N-1).Теперь вы должны быть остановлены непосредственно перед сбоем, и, поскольку вы все еще не находитесь внутри pow, у GDB не должно возникнуть проблем с отображением трассировки стека.

    Этот подход работает только в том случае, если ваша программа является детерминированной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...