Бесконечное прерывание() в обратном случае сбрасывает ядро ​​программы С++

У меня странная проблема, которую я не могу решить. Пожалуйста, помогите!

Программа представляет собой многопоточное приложение С++, работающее на машине ARM Linux. Недавно я начал тестировать его для длинных прогонов, и иногда он падает через 1-2 дня:

*** glibc detected ** /root/client/my_program: free(): invalid pointer: 0x002a9408 ***

Когда я открываю дамп ядра, я вижу, что основной поток, кажется, имеет поврежденный стек: все, что я вижу, это бесконечные вызовы abort().

GNU gdb (GDB) 7.3 
...
This GDB was configured as "--host=i686 --target=arm-linux".
[New LWP 706]
[New LWP 700]
[New LWP 702]
[New LWP 703]
[New LWP 704]
[New LWP 705]
Core was generated by `/root/client/my_program'.
Program terminated with signal 6, Aborted.
#0  0x001c44d4 in raise ()
(gdb) bt
#0  0x001c44d4 in raise ()
#1  0x001c47e0 in abort ()
#2  0x001c47e0 in abort ()
#3  0x001c47e0 in abort ()
#4  0x001c47e0 in abort ()
#5  0x001c47e0 in abort ()
#6  0x001c47e0 in abort ()
#7  0x001c47e0 in abort ()
#8  0x001c47e0 in abort ()
#9  0x001c47e0 in abort ()
#10 0x001c47e0 in abort ()
#11 0x001c47e0 in abort ()

И это продолжается и продолжается. Я попытался дотянуться до него, переместив стек: frame 3000 или даже больше, но в конечном итоге ядро ​​дампа закончилось из кадров, и я до сих пор не могу понять, почему это произошло.

Когда я просматриваю другие темы, все кажется нормальным.

(gdb) info threads
  Id   Target Id         Frame 
  6    LWP 705           0x00132f04 in nanosleep ()
  5    LWP 704           0x001e7a70 in select ()
  4    LWP 703           0x00132f04 in nanosleep ()
  3    LWP 702           0x00132318 in sem_wait ()
  2    LWP 700           0x00132f04 in nanosleep ()
* 1    LWP 706           0x001c44d4 in raise ()
(gdb) thread 5
[Switching to thread 5 (LWP 704)]
#0  0x001e7a70 in select ()
(gdb) bt
#0  0x001e7a70 in select ()
#1  0x00057ad4 in CSerialPort::read (this=0xbea7d98c, string_buffer=..., delimiter=..., timeout_ms=1000) at CSerialPort.cpp:202
#2  0x00070de4 in CScanner::readResponse (this=0xbea7d4cc, resp_recv=..., timeout=1000, delim=...) at PidScanner.cpp:657
#3  0x00071198 in CScanner::sendExpect (this=0xbea7d4cc, cmd=..., exp_str=..., rcv_str=..., timeout=1000) at PidScanner.cpp:604
#4  0x00071d48 in CScanner::pollPid (this=0xbea7d4cc, mode=1, pid=12, pid_str=...) at PidScanner.cpp:525
#5  0x00072ce0 in CScanner::poll1 (this=0xbea7d4cc) 
#6  0x00074c78 in CScanner::Poll (this=0xbea7d4cc) 
#7  0x00089edc in CThread5::Thread5Poll (this=0xbea7d360) 
#8  0x0008c140 in CThread5::run (this=0xbea7d360) 
#9  0x00088698 in CThread::threadFunc (p=0xbea7d360) 
#10 0x0012e6a0 in start_thread ()
#11 0x001e90e8 in clone ()
#12 0x001e90e8 in clone ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

(Названия классов и функций немного странные, потому что я их изменил -:) Итак, поток # 1 - это то, где стек поврежден, обратная трассировка всех остальных (2-6) показывает

Backtrace stopped: previous frame identical to this frame (corrupt stack?).

Это происходит потому, что потоки 2-6 создаются в потоке # 1.

Дело в том, что я не могу запустить программу в gdb, потому что она работает во встроенной системе. Я не могу использовать удаленный сервер gdb. Единственный вариант - проверка дампов ядра, которые происходят не очень часто.

Не могли бы вы предложить что-то, что могло бы переместить меня с этим? (Может быть, что-то еще я могу извлечь из основного дампа или, возможно, каким-то образом сделать некоторые крючки в коде, чтобы поймать вызов abort()).

UPDATE: Basile Starynkevitch предложил использовать Valgrind, но оказывается, что он портирован только для ARMv7. У меня ARM 926, который ARMv5, так что это не сработает для меня. Есть несколько попыток скомпилировать valgrind для ARMv5: компиляция Valgrind для ARMv5tel, valgrind на ARM9

ОБНОВЛЕНИЕ 2: Не удалось заставить Electric Fence работать с моей программой. Программа использует С++ и pthreads. Версия Efence, которую я получил, 2.1.13 разбился в произвольном месте после того, как я начал поток и попытался сделать что-то более или менее сложное ( например, чтобы поместить значение в вектор STL). Я видел, как люди отмечали некоторые исправления для Efence в Интернете, но не успели их попробовать. Я пробовал это на своем Linux-ПК, а не на ARM, а другие инструменты, такие как valgrind или Dmalloc, не сообщают о каких-либо проблемах с кодом. Таким образом, все, кто использует версию 2.1.13, могут быть готовы к возникновению проблем с pthreads (или, может быть, pthread + С++ + STL, не знаю).

Ответ 1

Мое предположение для "бесконечных" прерываний состоит в том, что либо abort() вызывает цикл (например, abort → обработчик сигналов → abort → ...), либо gdb не может правильно интерпретировать фреймы в стеке.

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

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

Ответ 2

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

Если вы можете воспроизвести проблему по своему усмотрению, вам нужно будет запустить поток под gdb и посмотреть, что происходит именно в тот момент, когда стек получает nuked. Это, в свою очередь, может потребовать какого-то тщательного поиска, чтобы определить, где именно происходит ошибка.

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